diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/actions/getFrameworksAction.ts b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/actions/getFrameworksAction.ts deleted file mode 100644 index 0f3b80b045..0000000000 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/actions/getFrameworksAction.ts +++ /dev/null @@ -1,62 +0,0 @@ -"use server"; - -import { db } from "@bubba/db"; -import { authActionClient } from "@/actions/safe-action"; -import type { - Framework, - OrganizationControl, - OrganizationFramework, -} from "@bubba/db/types"; -import { z } from "zod"; -import type { ActionData } from "@/actions/types"; - -type FrameworkWithControls = OrganizationFramework & { - organizationControl: OrganizationControl[]; - framework: Framework; -}; - -export interface FrameworksResponse { - frameworks: FrameworkWithControls[]; - availableFrameworks: Framework[]; -} - -export const getFrameworksAction = authActionClient - .schema(z.void()) - .metadata({ - name: "getFrameworks", - track: { - event: "get-frameworks", - channel: "server", - }, - }) - .action(async ({ ctx }): Promise> => { - const { user } = ctx; - - if (!user.organizationId) { - return { - error: "Not authorized - no organization found", - }; - } - - try { - const [frameworks, availableFrameworks] = await Promise.all([ - db.organizationFramework.findMany({ - where: { organizationId: user.organizationId }, - include: { - organizationControl: true, - framework: true, - }, - }), - db.framework.findMany(), - ]); - - return { - data: { frameworks, availableFrameworks }, - }; - } catch (error) { - console.error("Error fetching frameworks:", error); - return { - error: "Failed to fetch frameworks", - }; - } - }); diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/actions/selectFrameworksAction.ts b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/actions/selectFrameworksAction.ts deleted file mode 100644 index 12bc32e157..0000000000 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/actions/selectFrameworksAction.ts +++ /dev/null @@ -1,315 +0,0 @@ -"use server"; - -import { db } from "@bubba/db"; -import { authActionClient } from "@/actions/safe-action"; -import { z } from "zod"; -import type { ActionData } from "@/actions/types"; -import type { InputJsonValue } from "@prisma/client/runtime/library"; -import { RequirementType, type Policy, type User } from "@bubba/db/types"; - -const selectFrameworksSchema = z.object({ - frameworkIds: z.array(z.string()), -}); - -export const selectFrameworksAction = authActionClient - .schema(selectFrameworksSchema) - .metadata({ - name: "select-frameworks", - track: { - event: "select-frameworks", - channel: "server", - }, - }) - .action(async ({ parsedInput, ctx }): Promise> => { - const { frameworkIds } = parsedInput; - const { user } = ctx; - - if (!user.organizationId) { - return { - error: "Not authorized - no organization found", - }; - } - - try { - // Create categories - await createOrganizationCategories(user as User, frameworkIds); - - // Create frameworks and controls - const organizationFrameworks = await Promise.all( - frameworkIds.map((frameworkId) => - createOrganizationFramework(user as User, frameworkId) - ) - ); - - // Create policies - await createOrganizationPolicy(user as User, frameworkIds); - - // Create organization evidence first - await createOrganizationEvidence(user as User, frameworkIds); - - // Create control requirements after evidence is created - await createOrganizationControlRequirements( - user as User, - organizationFrameworks.map((framework) => framework.id) - ); - - return { - data: true, - }; - } catch (error) { - console.error("Error selecting frameworks:", error); - return { - error: "Failed to select frameworks", - }; - } - }); - -const createOrganizationFramework = async (user: User, frameworkId: string) => { - if (!user.organizationId) { - throw new Error("Not authorized - no organization found"); - } - - // Connect the framework to the organization. - const organizationFramework = await db.organizationFramework.create({ - data: { - organizationId: user.organizationId, - frameworkId, - status: "not_started", - }, - select: { - id: true, - }, - }); - - // Get the framework categories and their corresponding organization categories - const frameworkCategories = await db.frameworkCategory.findMany({ - where: { frameworkId }, - include: { - controls: true, - }, - }); - - // Get the organization categories that were just created - const organizationCategories = await db.organizationCategory.findMany({ - where: { - organizationId: user.organizationId, - frameworkId, - }, - }); - - // Create controls for each category - for (const frameworkCategory of frameworkCategories) { - const organizationCategory = organizationCategories.find( - (oc) => oc.name === frameworkCategory.name - ); - - if (!organizationCategory) continue; - - await db.organizationControl.createMany({ - data: frameworkCategory.controls.map((control) => ({ - organizationFrameworkId: organizationFramework.id, - controlId: control.id, - organizationId: user.organizationId!, - status: "not_started", - organizationCategoryId: organizationCategory.id, - })), - }); - } - - return organizationFramework; -}; - -const createOrganizationPolicy = async (user: User, frameworkIds: string[]) => { - if (!user.organizationId) { - throw new Error("Not authorized - no organization found"); - } - - const policies = await db.policy.findMany(); - const policiesForFrameworks: Policy[] = []; - - for (const policy of policies) { - const usedBy = policy.usedBy; - if (!usedBy) { - continue; - } - - const usedByFrameworkIds = Object.keys(usedBy); - - if ( - usedByFrameworkIds.some((frameworkId) => - frameworkIds.includes(frameworkId) - ) - ) { - policiesForFrameworks.push(policy); - } - } - - const organizationPolicies = await db.organizationPolicy.createMany({ - data: policiesForFrameworks.map((policy) => ({ - organizationId: user.organizationId!, - policyId: policy.id, - status: "draft", - content: policy.content as InputJsonValue[], - frequency: policy.frequency, - })), - }); - - return organizationPolicies; -}; - -const createOrganizationCategories = async ( - user: User, - frameworkIds: string[] -) => { - if (!user.organizationId) { - throw new Error("Not authorized - no organization found"); - } - - // For each frameworkCategory we need to get the controls. - const frameworkCategories = await db.frameworkCategory.findMany({ - where: { - frameworkId: { in: frameworkIds }, - }, - }); - - // Create the organization categories. - const organizationCategories = await db.organizationCategory.createMany({ - data: frameworkCategories.map((category) => ({ - name: category.name, - description: category.description, - organizationId: user.organizationId!, - frameworkId: category.frameworkId, - })), - }); - - return organizationCategories; -}; - -const createOrganizationControlRequirements = async ( - user: User, - organizationFrameworkIds: string[] -) => { - if (!user.organizationId) { - throw new Error("Not authorized - no organization found"); - } - - const controls = await db.organizationControl.findMany({ - where: { - organizationId: user.organizationId!, - organizationFrameworkId: { - in: organizationFrameworkIds, - }, - }, - include: { - control: true, - }, - }); - - // Create control requirements for each control - const controlRequirements = await db.controlRequirement.findMany({ - where: { - controlId: { in: controls.map((control) => control.controlId) }, - }, - include: { - policy: true, // Include the policy to get its ID - evidence: true, // Include the evidence to get its ID - }, - }); - - // Get all organization policies for this organization - const organizationPolicies = await db.organizationPolicy.findMany({ - where: { - organizationId: user.organizationId, - }, - }); - - // Get all organization evidences for this organization - const organizationEvidences = await db.organizationEvidence.findMany({ - where: { - organizationId: user.organizationId, - }, - }); - - for (const control of controls) { - const requirements = controlRequirements.filter( - (req) => req.controlId === control.controlId - ); - - await db.organizationControlRequirement.createMany({ - data: requirements.map((requirement) => { - // Find the corresponding organization policy if this is a policy requirement - const policyId = - requirement.type === "policy" ? requirement.policy?.id : null; - const organizationPolicy = policyId - ? organizationPolicies.find((op) => op.policyId === policyId) - : null; - - const evidenceId = - requirement.type === "evidence" ? requirement.evidenceId : null; - - console.log({ - evidenceId, - }); - - const organizationEvidence = evidenceId - ? organizationEvidences.find((e) => e.evidenceId === evidenceId) - : null; - - console.log({ - organizationEvidence, - }); - - return { - organizationControlId: control.id, - controlRequirementId: requirement.id, - type: requirement.type, - description: requirement.description, - organizationPolicyId: organizationPolicy?.id || null, - organizationEvidenceId: organizationEvidence?.id || null, - }; - }), - }); - } - - return controlRequirements; -}; - -const createOrganizationEvidence = async ( - user: User, - frameworkIds: string[] -) => { - if (!user.organizationId) { - throw new Error("Not authorized - no organization found"); - } - - const controlRequirements = await db.controlRequirement.findMany({ - where: { - type: RequirementType.evidence, - }, - include: { - control: { - include: { - frameworkCategory: { - include: { - framework: true, - }, - }, - }, - }, - }, - }); - - const organizationEvidence = await db.organizationEvidence.createMany({ - data: controlRequirements.map((evidence) => ({ - organizationId: user.organizationId!, - evidenceId: evidence.id, - name: evidence.name, - description: evidence.description, - frequency: evidence.frequency, - frameworkId: evidence.control.frameworkCategory?.framework.id || "", - assigneeId: user.id, - })), - }); - - return organizationEvidence; -}; diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/components/FrameworksGrid.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/components/FrameworksGrid.tsx index 37d0cd0db8..41278aac9e 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/components/FrameworksGrid.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/components/FrameworksGrid.tsx @@ -3,11 +3,11 @@ import { useI18n } from "@/locales/client"; import type { Framework } from "@bubba/db/types"; import { - Card, - CardContent, - CardFooter, - CardHeader, - CardTitle, + Card, + CardContent, + CardFooter, + CardHeader, + CardTitle, } from "@bubba/ui/card"; import { Button } from "@bubba/ui/button"; import { Checkbox } from "@bubba/ui/checkbox"; @@ -15,115 +15,115 @@ import { useState } from "react"; import { cn } from "@bubba/ui/cn"; interface FrameworkGridProps { - frameworks: Framework[]; - onSubmit: (selectedFrameworks: string[]) => Promise; + frameworks: Framework[]; + onSubmit: (selectedFrameworks: string[]) => void; } export function FrameworkGrid({ frameworks, onSubmit }: FrameworkGridProps) { - const t = useI18n(); - const [selectedFrameworks, setSelectedFrameworks] = useState([]); - const [isLoading, setIsLoading] = useState(false); - const [isSelecting, setIsSelecting] = useState(false); + const t = useI18n(); + const [selectedFrameworks, setSelectedFrameworks] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const [isSelecting, setIsSelecting] = useState(false); - /** - * Toggles the selection state of a framework. - * If the framework is already selected, it removes it from the selection. - * If the framework is not selected, it adds it to the selection. - * - * @param frameworkId - The ID of the framework to toggle - */ - const handleFrameworkToggle = (frameworkId: string) => { - setSelectedFrameworks((prev) => - prev.includes(frameworkId) - ? prev.filter((id) => id !== frameworkId) - : [...prev, frameworkId], - ); - }; + /** + * Toggles the selection state of a framework. + * If the framework is already selected, it removes it from the selection. + * If the framework is not selected, it adds it to the selection. + * + * @param frameworkId - The ID of the framework to toggle + */ + const handleFrameworkToggle = (frameworkId: string) => { + setSelectedFrameworks((prev) => + prev.includes(frameworkId) + ? prev.filter((id) => id !== frameworkId) + : [...prev, frameworkId], + ); + }; - const handleSubmit = async () => { - if (selectedFrameworks.length === 0) return; + const handleSubmit = async () => { + if (selectedFrameworks.length === 0) return; - setIsLoading(true); - try { - await onSubmit(selectedFrameworks); - } catch (error) { - console.error("Error selecting frameworks:", error); - } finally { - setIsLoading(false); - } - }; + setIsLoading(true); + try { + await onSubmit(selectedFrameworks); + } catch (error) { + console.error("Error selecting frameworks:", error); + } finally { + setIsLoading(false); + } + }; - if (!frameworks.length && !isSelecting) { - return ( -
-

- {t("frameworks.overview.grid.welcome.title")} -

-

- {t("frameworks.overview.grid.welcome.description")} -

- -
- ); - } + if (!frameworks.length && !isSelecting) { + return ( +
+

+ {t("frameworks.overview.grid.welcome.title")} +

+

+ {t("frameworks.overview.grid.welcome.description")} +

+ +
+ ); + } - return ( - - - {t("frameworks.overview.grid.title")} - - -
- {frameworks.map((framework) => ( -
handleFrameworkToggle(framework.id)} - > -
-
-

{framework.name}

-

- {framework.description} -

-

- {`${t("frameworks.overview.grid.version")}: ${framework.version}`} -

-
- e.stopPropagation()} - onCheckedChange={() => handleFrameworkToggle(framework.id)} - /> -
-
- ))} -
-
- - - - -
- ); + return ( + + + {t("frameworks.overview.grid.title")} + + +
+ {frameworks.map((framework) => ( +
handleFrameworkToggle(framework.id)} + > +
+
+

{framework.name}

+

+ {framework.description} +

+

+ {`${t("frameworks.overview.grid.version")}: ${framework.version}`} +

+
+ e.stopPropagation()} + onCheckedChange={() => handleFrameworkToggle(framework.id)} + /> +
+
+ ))} +
+
+ + + + +
+ ); } diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/components/FrameworksOverview.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/components/FrameworksOverview.tsx index 9b2069b356..1831d88465 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/components/FrameworksOverview.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/components/FrameworksOverview.tsx @@ -1,66 +1,27 @@ "use client"; -import { useI18n } from "@/locales/client"; -import { Skeleton } from "@bubba/ui/skeleton"; -import { useFrameworks } from "../hooks/useFrameworks"; +import type { + Framework, + OrganizationControl, + OrganizationFramework, +} from "@bubba/db/types"; import { FrameworkProgress } from "./FrameworkProgress"; -import { FrameworkGrid } from "./FrameworksGrid"; import { RequirementStatus } from "./RequirementStatusChart"; -export const FrameworksOverview = () => { - const t = useI18n(); - const { - frameworks, - availableFrameworks, - isLoading, - error, - selectFrameworks, - } = useFrameworks(); - - if (error) { - return ( -
-

{t("frameworks.overview.error")}

-
- ); - } - - if (isLoading) { - return ( -
-
- - -
-
- ); - } - - const hasFramework = frameworks.length > 0; - - if (hasFramework) { - return ( -
-
- - -
-
- ); - } - - return ( -
-

- {t("frameworks.overview.empty.title")} -

-

- {t("frameworks.overview.empty.description")} -

- -
- ); +export const FrameworksOverview = ({ + frameworks, +}: { + frameworks: (OrganizationFramework & { + organizationControl: OrganizationControl[]; + framework: Framework; + })[]; +}) => { + return ( +
+
+ + +
+
+ ); }; diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/hooks/useFrameworks.ts b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/hooks/useFrameworks.ts deleted file mode 100644 index 807531b1d2..0000000000 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/hooks/useFrameworks.ts +++ /dev/null @@ -1,64 +0,0 @@ -"use client"; - -import { useCallback, useState } from "react"; -import useSWR from "swr"; -import { getFrameworksAction } from "../actions/getFrameworksAction"; -import { selectFrameworksAction } from "../actions/selectFrameworksAction"; - -async function fetchFrameworks() { - const result = await getFrameworksAction(); - - if (!result) { - throw new Error("Failed to fetch frameworks"); - } - - const data = result.data?.data; - - if (!data) { - throw new Error("Invalid response from server"); - } - - return data; -} - -export function useFrameworks() { - const { - data, - error, - isLoading, - mutate: revalidateFrameworks, - } = useSWR("frameworks", () => fetchFrameworks()); - - const [isMutating, setIsMutating] = useState(false); - - const selectFrameworks = useCallback( - async (frameworkIds: string[]) => { - setIsMutating(true); - try { - const result = await selectFrameworksAction({ frameworkIds }); - - if (!result?.data) { - throw new Error("Failed to select frameworks"); - } - - await revalidateFrameworks(); - } catch (err) { - console.error("selectFrameworksAction failed:", err); - throw err; - } finally { - setIsMutating(false); - } - }, - [revalidateFrameworks] - ); - - return { - frameworks: data?.frameworks ?? [], - availableFrameworks: data?.availableFrameworks ?? [], - isLoading, - isMutating, - error, - revalidateFrameworks, - selectFrameworks, - }; -} diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/overview/frameworks/[frameworkId]/components/table/FrameworkControlsTableColumns.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/overview/frameworks/[frameworkId]/components/table/FrameworkControlsTableColumns.tsx index 3407b7eba8..e7b131ba7d 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/overview/frameworks/[frameworkId]/components/table/FrameworkControlsTableColumns.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/overview/frameworks/[frameworkId]/components/table/FrameworkControlsTableColumns.tsx @@ -73,7 +73,7 @@ export function FrameworkControlsTableColumns(): ColumnDef {row.original.name} diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/overview/frameworks/controls/[id]/components/SingleControl.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/overview/frameworks/controls/[id]/components/SingleControl.tsx index cae5e44092..5b89aa0e90 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/overview/frameworks/controls/[id]/components/SingleControl.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/overview/frameworks/controls/[id]/components/SingleControl.tsx @@ -1,22 +1,32 @@ "use client"; import { DisplayFrameworkStatus } from "@/components/frameworks/framework-status"; +import type { + Control, + OrganizationControl, + OrganizationControlRequirement, + OrganizationEvidence, + OrganizationPolicy, +} from "@bubba/db/types"; import { Card, CardContent, CardHeader, CardTitle } from "@bubba/ui/card"; import { useMemo } from "react"; -import { useOrganizationControl } from "../hooks/useOrganizationControl"; import { useOrganizationControlProgress } from "../hooks/useOrganizationControlProgress"; -import { ControlRequirementsTable } from "./table/ControlRequirementsTable"; import { SingleControlSkeleton } from "./SingleControlSkeleton"; +import { ControlRequirementsTable } from "./table/ControlRequirementsTable"; interface SingleControlProps { - controlId: string; + organizationControl: OrganizationControl & { + control: Control; + OrganizationControlRequirement: (OrganizationControlRequirement & { + organizationPolicy: OrganizationPolicy | null; + organizationEvidence: OrganizationEvidence | null; + })[]; + }; } -export const SingleControl = ({ controlId }: SingleControlProps) => { - const { data: control, isLoading: isControlLoading } = - useOrganizationControl(controlId); +export const SingleControl = ({ organizationControl }: SingleControlProps) => { const { data: controlProgress, isLoading: isControlProgressLoading } = - useOrganizationControlProgress(controlId); + useOrganizationControlProgress(organizationControl.id); const progressStatus = useMemo(() => { if (!controlProgress) return "not_started"; @@ -28,10 +38,7 @@ export const SingleControl = ({ controlId }: SingleControlProps) => { : "completed"; }, [controlProgress]); - if ( - (!control && isControlLoading) || - (!controlProgress && isControlProgressLoading) - ) { + if (!organizationControl || (!controlProgress && isControlProgressLoading)) { return ; } @@ -42,12 +49,14 @@ export const SingleControl = ({ controlId }: SingleControlProps) => { - {control?.control.name} + {organizationControl.control.name} -

{control?.control.description}

+

+ {organizationControl.control.description} +

@@ -55,15 +64,15 @@ export const SingleControl = ({ controlId }: SingleControlProps) => { Domain -

{control?.control.domain}

+

{organizationControl.control.domain}

- {control?.OrganizationControlRequirement && ( + {organizationControl.OrganizationControlRequirement && ( )}
diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/overview/frameworks/controls/[id]/components/table/ControlRequirementsTable.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/overview/frameworks/controls/[id]/components/table/ControlRequirementsTable.tsx index 8b6972d6b9..0ae842f9f2 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/overview/frameworks/controls/[id]/components/table/ControlRequirementsTable.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/overview/frameworks/controls/[id]/components/table/ControlRequirementsTable.tsx @@ -49,6 +49,9 @@ export function ControlRequirementsTable({ data }: DataTableProps) { } break; case "evidence": + console.log({ + requirement, + }); if (requirement.organizationEvidenceId) { router.push( `/${orgId}/evidence/${requirement.organizationEvidenceId}`, diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/overview/frameworks/controls/[id]/components/table/ControlRequirementsTableColumns.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/overview/frameworks/controls/[id]/components/table/ControlRequirementsTableColumns.tsx index b756d85213..54321d8cb6 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/overview/frameworks/controls/[id]/components/table/ControlRequirementsTableColumns.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/overview/frameworks/controls/[id]/components/table/ControlRequirementsTableColumns.tsx @@ -1,5 +1,6 @@ "use client"; +import type { OrganizationControlRequirement } from "@bubba/db/types"; import type { ColumnDef } from "@tanstack/react-table"; import { CheckCircle2, XCircle } from "lucide-react"; import type { RequirementTableData } from "./ControlRequirementsTable"; diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/overview/frameworks/controls/[id]/page.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/overview/frameworks/controls/[id]/page.tsx index 54c5ad6759..97f18c0c60 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/overview/frameworks/controls/[id]/page.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/overview/frameworks/controls/[id]/page.tsx @@ -1,11 +1,46 @@ +import { db } from "@bubba/db"; import { SingleControl } from "./components/SingleControl"; +import { auth } from "@/auth"; +import { redirect } from "next/navigation"; interface PageProps { - params: Promise<{ id: string }>; + params: Promise<{ id: string }>; } export default async function SingleControlPage({ params }: PageProps) { - const { id } = await params; + const { id } = await params; - return ; + const session = await auth(); + + if (!session?.user.organizationId) { + redirect("/"); + } + + const organizationControl = await getControl(id, session.user.organizationId); + + if (!organizationControl) { + redirect("/"); + } + + return ; } + +const getControl = async (id: string, organizationId: string) => { + const organizationControl = await db.organizationControl.findUnique({ + where: { + organizationId, + id, + }, + include: { + control: true, + OrganizationControlRequirement: { + include: { + organizationPolicy: true, + organizationEvidence: true, + }, + }, + }, + }); + + return organizationControl; +}; diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/page.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/page.tsx index 63c5148c36..8191e2b662 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/page.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/page.tsx @@ -2,6 +2,9 @@ import { getI18n } from "@/locales/server"; import type { Metadata } from "next"; import { setStaticParamsLocale } from "next-international/server"; import { FrameworksOverview } from "./components/FrameworksOverview"; +import { db } from "@bubba/db"; +import { auth } from "@/auth"; +import { redirect } from "next/navigation"; export default async function DashboardPage({ params, @@ -11,7 +14,16 @@ export default async function DashboardPage({ const { locale } = await params; setStaticParamsLocale(locale); - return ; + const session = await auth(); + const organizationId = session?.user.organizationId; + + if (!organizationId) { + redirect("/"); + } + + const frameworks = await getFrameworks(organizationId); + + return ; } export async function generateMetadata({ @@ -27,3 +39,15 @@ export async function generateMetadata({ title: t("sidebar.overview"), }; } + +const getFrameworks = async (organizationId: string) => { + const frameworks = await db.organizationFramework.findMany({ + where: { organizationId: organizationId }, + include: { + organizationControl: true, + framework: true, + }, + }); + + return frameworks; +}; diff --git a/apps/app/src/auth/config.ts b/apps/app/src/auth/config.ts index a35ba384a3..c810605342 100644 --- a/apps/app/src/auth/config.ts +++ b/apps/app/src/auth/config.ts @@ -57,13 +57,21 @@ export const authConfig: NextAuthConfig = { return !!auth; }, session: async ({ session, user }) => { - const organizationMemberCount = await db.organizationMember.count({ - where: { - userId: user.id, - organizationId: user.organizationId, - OR: [{ role: "admin" }, { role: "owner" }], - }, - }); + // Default to false for isAdmin + let isAdmin = false; + + // Only check for admin status if user has an organizationId + if (user.id && user.organizationId) { + const organizationMemberCount = await db.organizationMember.count({ + where: { + userId: user.id, + organizationId: user.organizationId, + OR: [{ role: "admin" }, { role: "owner" }], + }, + }); + + isAdmin = organizationMemberCount > 0; + } return { ...session, @@ -72,7 +80,7 @@ export const authConfig: NextAuthConfig = { id: user.id, organizationId: user.organizationId, role: user.role, - isAdmin: organizationMemberCount > 0, + isAdmin, }, }; }, diff --git a/apps/app/src/components/forms/policies/policy-overview.tsx b/apps/app/src/components/forms/policies/policy-overview.tsx index 31f9dd2f0f..081fdb12b9 100644 --- a/apps/app/src/components/forms/policies/policy-overview.tsx +++ b/apps/app/src/components/forms/policies/policy-overview.tsx @@ -3,11 +3,7 @@ import { updatePolicyFormAction } from "@/actions/policies/update-policy-form-action"; import { updatePolicyFormSchema } from "@/actions/schema"; import { SelectUser } from "@/components/select-user"; -import { - STATUS_TYPES, - StatusPolicies, - type StatusType, -} from "@/components/status-policies"; +import { StatusPolicies, type StatusType } from "@/components/status-policies"; import { useI18n } from "@/locales/client"; import { Departments, @@ -15,13 +11,11 @@ import { type OrganizationPolicy, type Policy, type PolicyStatus, - type Risk, - RiskCategory, - RiskStatus, type User, } from "@bubba/db/types"; import { Button } from "@bubba/ui/button"; -import { Checkbox } from "@bubba/ui/checkbox"; +import { Calendar } from "@bubba/ui/calendar"; +import { cn } from "@bubba/ui/cn"; import { Form, FormControl, @@ -30,7 +24,7 @@ import { FormLabel, FormMessage, } from "@bubba/ui/form"; -import { Popover, PopoverTrigger, PopoverContent } from "@bubba/ui/popover"; +import { Popover, PopoverContent, PopoverTrigger } from "@bubba/ui/popover"; import { Select, SelectContent, @@ -39,15 +33,20 @@ import { SelectValue, } from "@bubba/ui/select"; import { zodResolver } from "@hookform/resolvers/zod"; +import { format } from "date-fns"; import { CalendarIcon, Loader2 } from "lucide-react"; +import { useSession } from "next-auth/react"; import { useAction } from "next-safe-action/hooks"; -import { format } from "date-fns"; -import { Calendar } from "@bubba/ui/calendar"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import type { z } from "zod"; -import { cn } from "@bubba/ui/cn"; -import { useSession } from "next-auth/react"; + +const policyStatuses: PolicyStatus[] = [ + "draft", + "published", + "archived", + "needs_review", +] as const; export function UpdatePolicyOverview({ organizationPolicy, @@ -149,7 +148,9 @@ export function UpdatePolicyOverview({ diff --git a/apps/app/src/components/tables/people/employee-status.tsx b/apps/app/src/components/tables/people/employee-status.tsx index 21095ee726..717cd088e4 100644 --- a/apps/app/src/components/tables/people/employee-status.tsx +++ b/apps/app/src/components/tables/people/employee-status.tsx @@ -24,7 +24,7 @@ export function EmployeeStatus({ status }: { status: EmployeeStatusType }) { className={cn("size-2.5")} style={{ backgroundColor: EMPLOYEE_STATUS_COLORS[status] }} /> - {t(`common.status.${status}`)} + {t(`people.status.${status}`)} ); } diff --git a/apps/app/src/jobs/tasks/organization/create-organization.ts b/apps/app/src/jobs/tasks/organization/create-organization.ts index a0d0ff0150..56edb3382d 100644 --- a/apps/app/src/jobs/tasks/organization/create-organization.ts +++ b/apps/app/src/jobs/tasks/organization/create-organization.ts @@ -124,8 +124,18 @@ const createOrganizationFramework = async ( throw new Error(`Framework with ID ${frameworkId} not found`); } - const organizationFramework = await db.organizationFramework.create({ - data: { + // Use upsert to handle the case where a record may already exist + const organizationFramework = await db.organizationFramework.upsert({ + where: { + organizationId_frameworkId: { + organizationId, + frameworkId, + }, + }, + update: { + status: "not_started", // Only update status if it already exists + }, + create: { organizationId, frameworkId, status: "not_started", @@ -135,7 +145,7 @@ const createOrganizationFramework = async ( }, }); - logger.info("Created organization framework", { + logger.info("Created/updated organization framework", { organizationId, frameworkId, organizationFrameworkId: organizationFramework.id, @@ -256,6 +266,11 @@ const createOrganizationControlRequirements = async ( throw new Error("Not authorized - no organization found"); } + logger.info("Creating organization control requirements", { + organizationId, + organizationFrameworkIds, + }); + const controls = await db.organizationControl.findMany({ where: { organizationId, @@ -276,6 +291,15 @@ const createOrganizationControlRequirements = async ( include: { policy: true, // Include the policy to get its ID evidence: true, // Include the evidence to get its ID + control: { + include: { + frameworkCategory: { + include: { + framework: true, + }, + }, + }, + }, }, }); @@ -293,47 +317,144 @@ const createOrganizationControlRequirements = async ( }, }); - for (const control of controls) { - const requirements = controlRequirements.filter( - (req) => req.controlId === control.controlId + logger.info("Found control requirements and related items", { + controls: controls.length, + controlRequirements: controlRequirements.length, + organizationPolicies: organizationPolicies.length, + organizationEvidences: organizationEvidences.length, + }); + + // Create any missing organization evidence records + const missingEvidenceRecords = []; + + // First check for existing records to avoid duplicates + const existingEvidenceIds = new Set( + organizationEvidences.map((e) => e.evidenceId) + ); + + for (const requirement of controlRequirements) { + if ( + requirement.type === "evidence" && + requirement.evidenceId && + requirement.evidence && + !existingEvidenceIds.has(requirement.evidenceId) + ) { + missingEvidenceRecords.push({ + organizationId, + evidenceId: requirement.evidenceId, + name: requirement.name, + description: requirement.description, + frequency: requirement.frequency, + frameworkId: requirement.control.frameworkCategory?.framework.id || "", + assigneeId: null, // No user provided in this function + department: requirement.department, + }); + // Add the new ID to our set so subsequent iterations don't duplicate it + existingEvidenceIds.add(requirement.evidenceId); + } + } + + // Create any missing evidence records + if (missingEvidenceRecords.length > 0) { + logger.info( + `Creating ${missingEvidenceRecords.length} missing organization evidence records` ); - await db.organizationControlRequirement.createMany({ - data: requirements.map((requirement) => { - // Find the corresponding organization policy if this is a policy requirement - const policyId = - requirement.type === "policy" ? requirement.policy?.id : null; - const organizationPolicy = policyId - ? organizationPolicies.find((op) => op.policyId === policyId) - : null; + await db.organizationEvidence.createMany({ + data: missingEvidenceRecords, + skipDuplicates: true, // Add this to ensure we don't create duplicates + }); - const evidenceId = - requirement.type === "evidence" ? requirement.evidenceId : null; + // Refresh the organization evidences collection + const refreshedEvidences = await db.organizationEvidence.findMany({ + where: { + organizationId, + }, + }); - console.log({ - evidenceId, - }); + organizationEvidences.length = 0; + organizationEvidences.push(...refreshedEvidences); + + logger.info( + `After creating missing records, now have ${organizationEvidences.length} organization evidence records` + ); + } - const organizationEvidence = evidenceId - ? organizationEvidences.find((e) => e.evidenceId === evidenceId) - : null; + let evidenceNotFoundCount = 0; + let evidenceFoundCount = 0; - console.log({ - organizationEvidence, - }); + for (const control of controls) { + const requirements = controlRequirements.filter( + (req) => req.controlId === control.controlId + ); + + const requirementsToCreate = requirements.map((requirement) => { + // For policy requirements + let organizationPolicyId = null; + if (requirement.type === "policy" && requirement.policyId) { + const organizationPolicy = organizationPolicies.find( + (op) => op.policyId === requirement.policyId + ); + + if (!organizationPolicy) { + logger.warn("Policy not found in organization policies", { + requirementId: requirement.id, + policyId: requirement.policyId, + }); + } else { + organizationPolicyId = organizationPolicy.id; + } + } + + // For evidence requirements + let organizationEvidenceId = null; + if (requirement.type === "evidence" && requirement.evidenceId) { + const organizationEvidence = organizationEvidences.find( + (oe) => oe.evidenceId === requirement.evidenceId + ); + + if (!organizationEvidence) { + logger.warn("Evidence not found in organization evidences", { + requirementId: requirement.id, + evidenceId: requirement.evidenceId, + organizationEvidenceIds: organizationEvidences.map( + (e) => e.evidenceId + ), + }); + evidenceNotFoundCount++; + } else { + organizationEvidenceId = organizationEvidence.id; + evidenceFoundCount++; + + logger.info("Successfully linked evidence", { + requirementId: requirement.id, + evidenceId: requirement.evidenceId, + organizationEvidenceId: organizationEvidence.id, + }); + } + } - return { - organizationControlId: control.id, - controlRequirementId: requirement.id, - type: requirement.type, - description: requirement.description, - organizationPolicyId: organizationPolicy?.id || null, - organizationEvidenceId: organizationEvidence?.id || null, - }; - }), + return { + organizationControlId: control.id, + controlRequirementId: requirement.id, + type: requirement.type, + description: requirement.description || "", + organizationPolicyId: organizationPolicyId, + organizationEvidenceId: organizationEvidenceId, + }; }); + + if (requirementsToCreate.length > 0) { + await db.organizationControlRequirement.createMany({ + data: requirementsToCreate, + }); + } } + logger.info( + `Evidence requirements found: ${evidenceFoundCount}, not found: ${evidenceNotFoundCount}` + ); + return controlRequirements; }; @@ -346,9 +467,19 @@ const createOrganizationEvidence = async ( throw new Error("Not authorized - no organization found"); } - const evidence = await db.controlRequirement.findMany({ + logger.info("Starting organization evidence creation", { + organizationId, + frameworkIds, + }); + + const controlRequirements = await db.controlRequirement.findMany({ where: { type: RequirementType.evidence, + control: { + frameworkCategory: { + frameworkId: { in: frameworkIds }, + }, + }, }, include: { control: { @@ -360,21 +491,107 @@ const createOrganizationEvidence = async ( }, }, }, + evidence: true, // Include evidence to get the actual evidence ID }, }); - const organizationEvidence = await db.organizationEvidence.createMany({ - data: evidence.map((evidence) => ({ + logger.info( + `Found ${controlRequirements.length} control requirements for evidence` + ); + + // Filter out requirements that don't have a properly linked evidence record + const validRequirements = controlRequirements.filter((req) => req.evidence); + + logger.info( + `Found ${validRequirements.length} valid requirements with evidence` + ); + logger.info("Evidence IDs to be created", { + evidenceIds: validRequirements.map((req) => req.evidenceId), + }); + + if (validRequirements.length === 0) { + logger.warn( + "No valid evidence requirements found with linked evidence records", + { + organizationId, + frameworkIds, + totalRequirements: controlRequirements.length, + } + ); + return { count: 0 }; + } + + logger.info("Creating organization evidence", { + organizationId, + frameworkIds, + validRequirements: validRequirements.length, + totalRequirements: controlRequirements.length, + }); + + // First check for existing records to avoid duplicates + const existingEvidences = await db.organizationEvidence.findMany({ + where: { organizationId, - evidenceId: evidence.id, - name: evidence.name, - description: evidence.description, - frequency: evidence.frequency, - frameworkId: evidence.control.frameworkCategory?.framework.id || "", - assigneeId: userId, - department: evidence.department, - })), + evidenceId: { + in: validRequirements + .map((req) => req.evidenceId) + .filter((id): id is string => id !== null), + }, + }, + select: { + evidenceId: true, + }, + }); + + const existingEvidenceIds = new Set( + existingEvidences.map((e) => e.evidenceId) + ); + const newRequirements = validRequirements.filter( + (req) => req.evidenceId && !existingEvidenceIds.has(req.evidenceId) + ); + + logger.info( + `Found ${existingEvidences.length} existing evidence records, creating ${newRequirements.length} new records` + ); + + if (newRequirements.length > 0) { + // Only create evidence records that don't already exist + const organizationEvidence = await db.organizationEvidence.createMany({ + data: newRequirements.map((req) => ({ + organizationId, + evidenceId: req.evidence!.id, // Use the actual evidence ID from the linked evidence + name: req.name, + description: req.description, + frequency: req.frequency, + frameworkId: req.control.frameworkCategory?.framework.id || "", + assigneeId: userId, + department: req.department, + })), + }); + + logger.info( + `Created ${organizationEvidence.count} new organization evidence records` + ); + } + + // Verify the evidence was created by querying + const createdEvidences = await db.organizationEvidence.findMany({ + where: { + organizationId, + evidenceId: { + in: validRequirements + .map((req) => req.evidenceId) + .filter((id): id is string => id !== null), + }, + }, + }); + + logger.info( + `Total evidence records: ${createdEvidences.length} organization evidence records` + ); + logger.info("Evidence IDs", { + createdEvidenceIds: createdEvidences.map((e) => e.evidenceId), }); - return organizationEvidence; + return { count: createdEvidences.length }; }; diff --git a/apps/app/src/locales/core/common.ts b/apps/app/src/locales/core/common.ts index 301f8cffc6..450645c658 100644 --- a/apps/app/src/locales/core/common.ts +++ b/apps/app/src/locales/core/common.ts @@ -20,6 +20,19 @@ export const common = { next: "Next", complete: "Complete", }, + employee: { + status: { + active: "Active", + inactive: "Inactive", + }, + }, + frequency: { + daily: "Daily", + weekly: "Weekly", + monthly: "Monthly", + quarterly: "Quarterly", + yearly: "Yearly", + }, assignee: { label: "Assignee", placeholder: "Select assignee", @@ -102,8 +115,7 @@ export const common = { dragDropOrClickToSelect: "Drag and drop or click to select file", maxFileSize: "Max file size: {size}MB", }, - fileUrl: { - }, + fileUrl: {}, fileCard: { preview: "Preview", filePreview: "File Preview: {fileName}", @@ -133,7 +145,8 @@ export const common = { toasts: { error: "Something went wrong, please try again.", error_uploading_files: "Cannot upload more than 1 file at a time", - error_uploading_files_multiple: "Cannot upload more files than the maximum allowed", + error_uploading_files_multiple: + "Cannot upload more files than the maximum allowed", error_no_files_selected: "No files selected", error_file_rejected: "File {file} was rejected", error_failed_to_upload_files: "Failed to upload files", @@ -146,9 +159,4 @@ export const common = { edit: "Edit", description: "Description", last_updated: "Last Updated", - frequency: { - monthly: "Monthly", - quarterly: "Quarterly", - yearly: "Yearly" - } -} as const \ No newline at end of file +} as const; diff --git a/apps/app/src/locales/features/people.ts b/apps/app/src/locales/features/people.ts index bc2acb081e..1b082198ed 100644 --- a/apps/app/src/locales/features/people.ts +++ b/apps/app/src/locales/features/people.ts @@ -4,6 +4,10 @@ export const people = { title: "Employee Details", tasks: "Tasks", }, + status: { + active: "Active", + inactive: "Inactive", + }, description: "Manage your team members and their roles.", filters: { search: "Search people...", @@ -53,4 +57,4 @@ export const people = { success: "Employee added successfully", error: "Failed to add employee", }, -} as const \ No newline at end of file +} as const; diff --git a/apps/app/src/locales/features/policies.ts b/apps/app/src/locales/features/policies.ts index e6f0a3069c..74ac79ec30 100644 --- a/apps/app/src/locales/features/policies.ts +++ b/apps/app/src/locales/features/policies.ts @@ -30,6 +30,10 @@ export const policies = { signature_not_required: "Do not ask employees to sign", signature_requirement: "Signature Requirement", signature_requirement_placeholder: "Select signature requirement", + policy_department: "Department", + policy_department_placeholder: "Select a department", + policy_status: "Policy Status", + policy_status_placeholder: "Select a policy status", }, }, new: { @@ -76,4 +80,5 @@ export const policies = { create_first: "Create first policy", last_updated: "Last updated: {{date}}", save: "Save", -} as const \ No newline at end of file + policy_details: "Policy Details", +} as const; diff --git a/apps/app/src/locales/settings/settings.ts b/apps/app/src/locales/settings/settings.ts index de19dc6589..6b1430014d 100644 --- a/apps/app/src/locales/settings/settings.ts +++ b/apps/app/src/locales/settings/settings.ts @@ -49,8 +49,7 @@ export const settings = { ninety_days: "90 days", one_year: "1 year", api_key: "API Key", - save_warning: - "This key will only be shown once. Make sure to copy it now.", + save_warning: "This key will only be shown once. Make sure to copy it now.", copied: "API key copied to clipboard", revoke_confirm: "Are you sure you want to revoke this API key? This action cannot be undone.", @@ -94,7 +93,7 @@ export const settings = { }, status: { accepted: "Accepted", - pending: "Pending" + pending: "Pending", }, role: { owner: "Owner", @@ -145,7 +144,7 @@ export const settings = { error: { title: "Invitation Error", description: "There was an error processing your invitation.", - home: "Go to Home" + home: "Go to Home", }, form: { email: { @@ -180,8 +179,7 @@ export const settings = { }, toast: { error: "Failed to send invitation", - unexpected: - "An unexpected error occurred while sending the invitation", + unexpected: "An unexpected error occurred while sending the invitation", }, }, member_actions: { @@ -219,4 +217,4 @@ export const settings = { }, }, }, -} as const \ No newline at end of file +} as const; diff --git a/packages/data/controls/soc2.json b/packages/data/controls/soc2.json index 5238b9c743..d0a7700a0f 100644 --- a/packages/data/controls/soc2.json +++ b/packages/data/controls/soc2.json @@ -9,18 +9,12 @@ { "id": "CC1.1-policy", "type": "policy", - "name": "Corporate Governance Policy", - "description": "Reference to the Corporate Governance Policy that defines board oversight responsibilities, roles, review frequency, and reporting requirements.", - "policyId": "corporate_governance", - "frequency": "yearly" + "policyId": "corporate_governance" }, { "id": "CC1.1-evidence", "type": "evidence", - "name": "Board Meeting Documentation", - "description": "Minutes of board meetings and oversight reports demonstrating active review of internal controls. Provide Board Oversight Procedures document that outlines scheduled reviews, risk assessments, and communication channels with management.", - "frequency": "quarterly", - "department": "admin" + "evidenceId": "board_meeting_documentation" } ] }, @@ -34,10 +28,7 @@ { "id": "CC1.2-policy", "type": "policy", - "name": "Corporate Governance Policy", - "description": "Reference to the Corporate Governance Policy that outlines management responsibilities and the organizational structure for effective oversight.", - "policyId": "corporate_governance", - "frequency": "yearly" + "policyId": "corporate_governance" }, { "id": "CC1.2-training", @@ -48,10 +39,7 @@ { "id": "CC1.2-evidence", "type": "evidence", - "name": "Management Structure Documentation", - "description": "Organizational charts, management meeting minutes, and training records. Provide Management Oversight Procedures document that outlines decision-making processes and periodic reporting.", - "frequency": "yearly", - "department": "admin" + "evidenceId": "management_structure_documentation" } ] }, @@ -65,18 +53,12 @@ { "id": "CC1.3-policy", "type": "policy", - "name": "Human Resources Policy", - "description": "Reference to the Human Resources Policy that defines recruitment, retention, and competency requirements.", - "policyId": "human_resources", - "frequency": "yearly" + "policyId": "human_resources" }, { "id": "CC1.3-evidence", "type": "evidence", - "name": "HR Documentation", - "description": "HR records, training logs, and performance evaluations. Provide Talent Management Procedures document that outlines talent acquisition, performance management, and professional development.", - "frequency": "yearly", - "department": "hr" + "evidenceId": "hr_documentation" } ] }, @@ -90,10 +72,7 @@ { "id": "CC1.4-policy", "type": "policy", - "name": "Human Resources Policy", - "description": "Reference to the Human Resources Policy that outlines roles, responsibilities, and disciplinary measures.", - "policyId": "human_resources", - "frequency": "yearly" + "policyId": "human_resources" }, { "id": "CC1.4-training", @@ -104,10 +83,7 @@ { "id": "CC1.4-evidence", "type": "evidence", - "name": "Personnel Compliance Documentation", - "description": "Employee acknowledgment forms, training records, and disciplinary documentation. Provide Personnel Accountability Procedures document that outlines monitoring, enforcing, and reviewing personnel accountability.", - "frequency": "yearly", - "department": "hr" + "evidenceId": "personnel_compliance_documentation" } ] }, @@ -121,10 +97,7 @@ { "id": "CC1.5-policy", "type": "policy", - "name": "Corporate Governance Policy", - "description": "Reference to the Corporate Governance Policy (or a dedicated Code of Conduct within it) that defines ethical behavior and compliance expectations.", - "policyId": "corporate_governance", - "frequency": "yearly" + "policyId": "corporate_governance" }, { "id": "CC1.5-training", @@ -135,10 +108,7 @@ { "id": "CC1.5-evidence", "type": "evidence", - "name": "Ethics Compliance Documentation", - "description": "Signed acknowledgment forms, training completion records, and records of investigations or disciplinary actions. Provide Ethics Compliance Procedures document that outlines reporting, investigating, and addressing breaches of the code.", - "frequency": "yearly", - "department": "hr" + "evidenceId": "ethics_compliance_documentation" } ] }, @@ -152,18 +122,12 @@ { "id": "CC2.1-policy", "type": "policy", - "name": "Information Security Policy", - "description": "Reference to the Information Security Policy that outlines data accuracy, completeness, and timeliness requirements.", - "policyId": "information_security", - "frequency": "yearly" + "policyId": "information_security" }, { "id": "CC2.1-evidence", "type": "evidence", - "name": "Data Quality Documentation", - "description": "Data quality reports, audit logs, and records of corrective actions. Provide Data Quality Procedures document that outlines validating data inputs and correcting errors throughout data processing.", - "frequency": "quarterly", - "department": "it" + "evidenceId": "data_quality_documentation" } ] }, @@ -177,18 +141,12 @@ { "id": "CC2.2-policy", "type": "policy", - "name": "Corporate Governance Policy", - "description": "Reference to the Corporate Governance Policy that includes guidelines for internal communications of control objectives.", - "policyId": "corporate_governance", - "frequency": "yearly" + "policyId": "corporate_governance" }, { "id": "CC2.2-evidence", "type": "evidence", - "name": "Communication Records", - "description": "Communication logs, email distributions, and meeting minutes. Provide Internal Communication Procedures document that outlines internal reporting, announcements, and feedback regarding internal controls.", - "frequency": "quarterly", - "department": "admin" + "evidenceId": "communication_records" } ] }, @@ -202,18 +160,12 @@ { "id": "CC2.3-policy", "type": "policy", - "name": "Corporate Governance Policy", - "description": "Reference to the Corporate Governance Policy that outlines external communication guidelines for control-related matters.", - "policyId": "corporate_governance", - "frequency": "yearly" + "policyId": "corporate_governance" }, { "id": "CC2.3-evidence", "type": "evidence", - "name": "External Communication Records", - "description": "Records of external communications, press releases, and stakeholder correspondence. Provide External Communication Procedures document that outlines drafting, approving, and disseminating external communications related to internal controls.", - "frequency": "yearly", - "department": "admin" + "evidenceId": "external_communication_records" } ] }, @@ -227,18 +179,12 @@ { "id": "CC3.1-policy", "type": "policy", - "name": "Risk Management Policy", - "description": "Reference to the Risk Management Policy that defines methodologies, frequency, and scope for risk assessments.", - "policyId": "risk_management", - "frequency": "yearly" + "policyId": "risk_management" }, { "id": "CC3.1-evidence", "type": "evidence", - "name": "Risk Assessment Documentation", - "description": "Risk assessment reports, risk registers, and management review minutes. Provide Risk Assessment Procedures document that outlines conducting regular risk assessments, documenting risks, and assigning risk owners.", - "frequency": "yearly", - "department": "gov" + "evidenceId": "risk_assessment_documentation" } ] }, @@ -252,18 +198,12 @@ { "id": "CC3.2-policy", "type": "policy", - "name": "Risk Management Policy", - "description": "Reference to the Risk Management Policy requiring systematic identification of risks across all business areas.", - "policyId": "risk_management", - "frequency": "yearly" + "policyId": "risk_management" }, { "id": "CC3.2-evidence", "type": "evidence", - "name": "Risk Identification Records", - "description": "Risk register entries, workshop records, and risk analysis documentation. Provide Risk Identification Procedures document that outlines risk identification using workshops, surveys, and data analysis.", - "frequency": "yearly", - "department": "gov" + "evidenceId": "risk_identification_records" } ] }, @@ -277,18 +217,12 @@ { "id": "CC3.3-policy", "type": "policy", - "name": "Risk Management Policy", - "description": "Reference to the Risk Management Policy with provisions for assessing and mitigating fraud risks.", - "policyId": "risk_management", - "frequency": "yearly" + "policyId": "risk_management" }, { "id": "CC3.3-evidence", "type": "evidence", - "name": "Fraud Risk Documentation", - "description": "Fraud risk assessment reports, internal audit findings, and remediation tracking records. Provide Fraud Risk Assessment Procedures document that outlines conducting fraud risk assessments and reporting findings.", - "frequency": "yearly", - "department": "gov" + "evidenceId": "fraud_risk_documentation" } ] }, @@ -302,18 +236,12 @@ { "id": "CC3.4-policy", "type": "policy", - "name": "Change Management Policy", - "description": "Reference to the Change Management Policy that outlines how risks associated with changes are evaluated.", - "policyId": "change_management", - "frequency": "yearly" + "policyId": "change_management" }, { "id": "CC3.4-evidence", "type": "evidence", - "name": "Change Risk Documentation", - "description": "Change impact assessments, risk logs, and management approval records. Provide Change Risk Assessment Procedures document that outlines assessing the impact of changes and planning mitigations for associated risks.", - "frequency": "yearly", - "department": "it" + "evidenceId": "change_risk_documentation" } ] }, @@ -327,18 +255,12 @@ { "id": "CC4.1-policy", "type": "policy", - "name": "Information Security Policy", - "description": "Reference to the Information Security Policy that outlines monitoring requirements for internal controls.", - "policyId": "information_security", - "frequency": "yearly" + "policyId": "information_security" }, { "id": "CC4.1-evidence", "type": "evidence", - "name": "Control Testing Documentation", - "description": "Internal audit reports, control testing records, and management review documentation. Provide Control Testing Procedures document that outlines ongoing and periodic testing of internal controls.", - "frequency": "quarterly", - "department": "gov" + "evidenceId": "control_testing_documentation" } ] }, @@ -352,18 +274,12 @@ { "id": "CC4.2-policy", "type": "policy", - "name": "Risk Management Policy", - "description": "Reference to the Risk Management Policy that establishes a framework for identifying, reporting, and remediating control deficiencies.", - "policyId": "risk_management", - "frequency": "yearly" + "policyId": "risk_management" }, { "id": "CC4.2-evidence", "type": "evidence", - "name": "Deficiency Management Records", - "description": "Deficiency logs, remediation plans, and follow-up audit reports. Provide Deficiency Management Procedures document that outlines documenting, tracking, and remediating control deficiencies.", - "frequency": "quarterly", - "department": "gov" + "evidenceId": "deficiency_management_records" } ] }, @@ -377,18 +293,12 @@ { "id": "CC5.1-policy", "type": "policy", - "name": "Information Security Policy", - "description": "Reference to the Information Security Policy that outlines criteria for the design and selection of controls.", - "policyId": "information_security", - "frequency": "yearly" + "policyId": "information_security" }, { "id": "CC5.1-evidence", "type": "evidence", - "name": "Control Implementation Records", - "description": "Control design documents, implementation records, and risk mitigation assessments. Provide Control Implementation Procedures document that outlines developing, documenting, and implementing controls to mitigate risks.", - "frequency": "yearly", - "department": "gov" + "evidenceId": "control_implementation_records" } ] }, @@ -402,18 +312,12 @@ { "id": "CC5.2-policy", "type": "policy", - "name": "Information Security Policy", - "description": "Reference to the Information Security Policy that specifies baseline security configurations and technology management practices.", - "policyId": "information_security", - "frequency": "yearly" + "policyId": "information_security" }, { "id": "CC5.2-evidence", "type": "evidence", - "name": "Technology Control Records", - "description": "System configuration records, monitoring reports, and technology audit logs. Provide Technology Control Procedures document that outlines implementing and monitoring technology controls such as firewalls, antivirus, and system hardening.", - "frequency": "quarterly", - "department": "it" + "evidenceId": "technology_control_records" } ] }, @@ -427,18 +331,12 @@ { "id": "CC5.3-policy", "type": "policy", - "name": "Corporate Governance Policy", - "description": "Reference to the Corporate Governance Policy that outlines how policies are communicated, enforced, and monitored.", - "policyId": "corporate_governance", - "frequency": "yearly" + "policyId": "corporate_governance" }, { "id": "CC5.3-evidence", "type": "evidence", - "name": "Policy Implementation Records", - "description": "Policy distribution records, training logs, and compliance monitoring reports. Provide Policy Implementation Procedures document that outlines operationalizing policies including documentation, training, and compliance monitoring.", - "frequency": "yearly", - "department": "gov" + "evidenceId": "policy_implementation_records" } ] }, @@ -452,18 +350,12 @@ { "id": "CC6.1-policy", "type": "policy", - "name": "Access Control Policy", - "description": "Reference to the Access Control Policy that defines controls for network and system access including segmentation and firewalls.", - "policyId": "access_control", - "frequency": "yearly" + "policyId": "access_control" }, { "id": "CC6.1-evidence", "type": "evidence", - "name": "Access Control Records", - "description": "Access control configurations, firewall logs, and system access review reports. Provide Access Management Procedures document that outlines granting, monitoring, and revoking system access including access logging and periodic reviews.", - "frequency": "quarterly", - "department": "it" + "evidenceId": "access_control_records" } ] }, @@ -477,18 +369,12 @@ { "id": "CC6.2-policy", "type": "policy", - "name": "Access Control Policy", - "description": "Reference to the Access Control Policy requiring strong authentication methods including passwords and multi-factor authentication.", - "policyId": "access_control", - "frequency": "yearly" + "policyId": "access_control" }, { "id": "CC6.2-evidence", "type": "evidence", - "name": "Authentication Records", - "description": "Authentication logs, MFA configuration records, and user account management records. Provide Authentication Management Procedures document that outlines user registration, credential issuance, and ongoing authentication management.", - "frequency": "quarterly", - "department": "it" + "evidenceId": "authentication_records" } ] }, @@ -502,18 +388,12 @@ { "id": "CC6.3-policy", "type": "policy", - "name": "Access Control Policy", - "description": "Reference to the Access Control Policy detailing prompt revocation of user access upon termination or role change.", - "policyId": "access_control", - "frequency": "yearly" + "policyId": "access_control" }, { "id": "CC6.3-evidence", "type": "evidence", - "name": "Access Removal Records", - "description": "User termination logs, de-provisioning records, and access review reports. Provide Access Removal Procedures document that outlines de-provisioning user access immediately after termination or role change.", - "frequency": "quarterly", - "department": "it" + "evidenceId": "access_removal_records" } ] }, @@ -527,18 +407,12 @@ { "id": "CC6.4-policy", "type": "policy", - "name": "Access Control Policy", - "description": "Reference to the Access Control Policy mandating periodic reviews of user access rights and privileges.", - "policyId": "access_control", - "frequency": "yearly" + "policyId": "access_control" }, { "id": "CC6.4-evidence", "type": "evidence", - "name": "Access Review Records", - "description": "Access review logs, user access reports, and management sign-off documentation. Provide Access Review Procedures document that outlines conducting regular access reviews and validating that user permissions align with current roles.", - "frequency": "quarterly", - "department": "it" + "evidenceId": "access_review_records" } ] }, @@ -552,18 +426,12 @@ { "id": "CC6.5-policy", "type": "policy", - "name": "Access Control Policy", - "description": "Reference to the Access Control Policy that defines processes for creating, maintaining, and terminating system accounts.", - "policyId": "access_control", - "frequency": "yearly" + "policyId": "access_control" }, { "id": "CC6.5-evidence", "type": "evidence", - "name": "Account Management Records", - "description": "Account provisioning logs, privileged account review reports, and system audit logs. Provide Account Management Procedures document that outlines provisioning system accounts, managing privileged access, and monitoring account activity.", - "frequency": "quarterly", - "department": "it" + "evidenceId": "account_management_records" } ] }, @@ -577,18 +445,12 @@ { "id": "CC6.6-policy", "type": "policy", - "name": "Access Control Policy", - "description": "Reference to the Access Control Policy that establishes requirements for securing facilities and restricting physical entry to sensitive areas.", - "policyId": "access_control", - "frequency": "yearly" + "policyId": "access_control" }, { "id": "CC6.6-evidence", "type": "evidence", - "name": "Physical Access Records", - "description": "Facility access logs, visitor sign-in records, and security camera reports. Provide Physical Access Procedures document that outlines issuing physical access credentials, managing visitor access, and monitoring facility entry.", - "frequency": "quarterly", - "department": "admin" + "evidenceId": "physical_access_records" } ] }, @@ -602,18 +464,12 @@ { "id": "CC6.7-policy", "type": "policy", - "name": "Change Management Policy", - "description": "Reference to the Change Management Policy that governs modifications to information assets.", - "policyId": "change_management", - "frequency": "yearly" + "policyId": "change_management" }, { "id": "CC6.7-evidence", "type": "evidence", - "name": "Change Management Records", - "description": "Change logs, approval records, and configuration management documentation. Provide Change Management Procedures document that outlines managing, reviewing, and approving changes to information assets, including rollback measures.", - "frequency": "yearly", - "department": "it" + "evidenceId": "change_management_records" } ] }, @@ -627,18 +483,12 @@ { "id": "CC6.8-policy", "type": "policy", - "name": "Information Security Policy", - "description": "Reference to the Information Security Policy that requires the use of antivirus, anti-malware, and threat detection solutions.", - "policyId": "information_security", - "frequency": "yearly" + "policyId": "information_security" }, { "id": "CC6.8-evidence", "type": "evidence", - "name": "Malware Prevention Records", - "description": "Antivirus logs, malware detection alerts, and remediation records. Provide Malware Prevention Procedures document that outlines scanning, detecting, and responding to malware incidents on endpoints and networks.", - "frequency": "quarterly", - "department": "it" + "evidenceId": "malware_prevention_records" } ] }, @@ -652,18 +502,12 @@ { "id": "CC7.1-policy", "type": "policy", - "name": "Information Security Policy", - "description": "Reference to the Information Security Policy that outlines monitoring requirements for infrastructure and systems.", - "policyId": "information_security", - "frequency": "yearly" + "policyId": "information_security" }, { "id": "CC7.1-evidence", "type": "evidence", - "name": "Infrastructure Monitoring Records", - "description": "Monitoring tool reports, alert logs, and incident tracking records. Provide Infrastructure Monitoring Procedures document that outlines real-time monitoring of infrastructure performance, security events, and system vulnerabilities.", - "frequency": "quarterly", - "department": "it" + "evidenceId": "infrastructure_monitoring_records" } ] }, @@ -677,18 +521,12 @@ { "id": "CC7.2-policy", "type": "policy", - "name": "Incident Response Policy", - "description": "Reference to the Incident Response Policy that defines roles, responsibilities, and procedures for addressing security events.", - "policyId": "incident_response", - "frequency": "yearly" + "policyId": "incident_response" }, { "id": "CC7.2-evidence", "type": "evidence", - "name": "Incident Response Records", - "description": "Incident logs, response drill reports, and post-incident review documentation. Provide Security Incident Response Procedures document that outlines detecting, containing, eradicating, and recovering from security incidents.", - "frequency": "yearly", - "department": "it" + "evidenceId": "incident_response_records" } ] }, @@ -702,18 +540,12 @@ { "id": "CC7.3-policy", "type": "policy", - "name": "Business Continuity Policy", - "description": "Reference to the Business Continuity & Disaster Recovery Policy that outlines strategies for restoring systems and data.", - "policyId": "business_continuity", - "frequency": "yearly" + "policyId": "business_continuity" }, { "id": "CC7.3-evidence", "type": "evidence", - "name": "Recovery Records", - "description": "Recovery test results, restoration logs, and incident recovery reports. Provide Recovery Procedures document that outlines restoring systems and data following a security incident, including backup restoration and validation.", - "frequency": "yearly", - "department": "it" + "evidenceId": "recovery_records" } ] }, @@ -727,17 +559,12 @@ { "id": "CC7.4-policy", "type": "policy", - "description": "Reference to the Incident Response Policy that requires root cause analysis for security incidents.", - "policyId": "incident_response", - "frequency": "yearly" + "policyId": "incident_response" }, { "id": "CC7.4-evidence", "type": "evidence", - "name": "Incident Analysis Records", - "description": "Incident analysis reports, lessons learned documentation, and remediation tracking records. Provide procedures document that outlines conducting root cause analysis and developing remediation plans for recurring issues.", - "frequency": "yearly", - "department": "it" + "evidenceId": "incident_analysis_records" } ] }, @@ -751,17 +578,12 @@ { "id": "CC7.5-policy", "type": "policy", - "description": "Reference to the Incident Response Policy that defines processes for internal and external incident notifications.", - "policyId": "incident_response", - "frequency": "yearly" + "policyId": "incident_response" }, { "id": "CC7.5-evidence", "type": "evidence", - "name": "Incident Communication Records", - "description": "Communication logs, notifications, and records of stakeholder communications during incidents. Provide procedures document that outlines timely communication of security incidents to stakeholders, regulators, and internal teams.", - "frequency": "yearly", - "department": "it" + "evidenceId": "incident_communication_records" } ] }, @@ -775,17 +597,12 @@ { "id": "CC8.1-policy", "type": "policy", - "description": "Reference to the Change Management Policy that establishes controls for reviewing, approving, and documenting changes.", - "policyId": "change_management", - "frequency": "yearly" + "policyId": "change_management" }, { "id": "CC8.1-evidence", "type": "evidence", - "name": "Change Request Logs", - "description": "Change request logs, approval records, and post-change review reports. Provide procedure for submitting, reviewing, and approving changes, including emergency change processes.", - "frequency": "yearly", - "department": "itsm" + "evidenceId": "change_request_logs" } ] }, @@ -799,17 +616,12 @@ { "id": "CC9.1-policy", "type": "policy", - "description": "Reference to the Business Continuity & Disaster Recovery Policy that outlines recovery objectives, strategies, and responsibilities.", - "policyId": "business_continuity", - "frequency": "yearly" + "policyId": "business_continuity" }, { "id": "CC9.1-evidence", "type": "evidence", - "name": "Business Continuity Plans", - "description": "Business continuity plans, BIA reports, and disaster recovery test results. Provide procedure for conducting business impact analyses, backup operations, and disaster recovery testing.", - "frequency": "yearly", - "department": "it" + "evidenceId": "business_continuity_plans" } ] }, @@ -823,17 +635,12 @@ { "id": "CC9.2-policy", "type": "policy", - "description": "Reference to the Vendor Risk Management Policy outlining criteria for vendor selection, risk assessment, and ongoing monitoring.", - "policyId": "vendor_risk_management", - "frequency": "yearly" + "policyId": "vendor_risk_management" }, { "id": "CC9.2-evidence", "type": "evidence", - "name": "Vendor Risk Assessment Records", - "description": "Vendor risk assessment reports, due diligence records, and contract reviews. Provide procedure for assessing vendor risk through questionnaires, audits, and continuous monitoring.", - "frequency": "yearly", - "department": "gov" + "evidenceId": "vendor_risk_assessment_records" } ] }, @@ -847,17 +654,12 @@ { "id": "CC9.9-policy", "type": "policy", - "description": "Reference to the Business Continuity & Disaster Recovery Policy that defines testing frequency, methodologies, and remediation procedures.", - "policyId": "business_continuity", - "frequency": "yearly" + "policyId": "business_continuity" }, { "id": "CC9.9-evidence", "type": "evidence", - "name": "Business Continuity and Disaster Recovery Testing Records", - "description": "Test reports, remediation logs, and updated BC/DR plans. Provide procedure for conducting regular BC/DR tests and documenting test results with follow-up actions.", - "frequency": "yearly", - "department": "it" + "evidenceId": "business_continuity_and_disaster_recovery_testing_records" } ] }, @@ -871,17 +673,12 @@ { "id": "A1.1-policy", "type": "policy", - "description": "Reference to the Availability Policy that outlines uptime, performance, and service level requirements.", - "policyId": "availability", - "frequency": "yearly" + "policyId": "availability" }, { "id": "A1.1-evidence", "type": "evidence", - "name": "Uptime Reports", - "description": "Uptime reports, incident logs, and SLA monitoring records. Provide procedure for monitoring system availability, reporting outages, and ensuring continuity of operations.", - "frequency": "quarterly", - "department": "itsm" + "evidenceId": "uptime_reports" } ] }, @@ -895,17 +692,12 @@ { "id": "A1.2-policy", "type": "policy", - "description": "Reference to the Availability Policy defining procedures for monitoring, forecasting, and managing system capacity.", - "policyId": "availability", - "frequency": "yearly" + "policyId": "availability" }, { "id": "A1.2-evidence", "type": "evidence", - "name": "Capacity Reports", - "description": "Capacity reports, trend analysis, and resource utilization logs. Provide procedure for capacity analysis, resource allocation, and performance tuning.", - "frequency": "quarterly", - "department": "it" + "evidenceId": "capacity_reports" } ] }, @@ -919,17 +711,12 @@ { "id": "A1.3-policy", "type": "policy", - "description": "Reference to the Business Continuity & Disaster Recovery Policy that outlines procedures to restore services after an outage.", - "policyId": "business_continuity", - "frequency": "yearly" + "policyId": "business_continuity" }, { "id": "A1.3-evidence", "type": "evidence", - "name": "Incident Recovery Records", - "description": "Incident recovery test results, post-incident reviews, and restoration logs. Provide procedure for incident response, recovery, and restoration of systems after an outage.", - "frequency": "yearly", - "department": "it" + "evidenceId": "incident_recovery_records" } ] }, @@ -943,17 +730,12 @@ { "id": "C1.1-policy", "type": "policy", - "description": "Reference to the Data Classification Policy that outlines classification levels and handling requirements for confidential information.", - "policyId": "data_classification", - "frequency": "yearly" + "policyId": "data_classification" }, { "id": "C1.1-evidence", "type": "evidence", - "name": "Data Classification Records", - "description": "Data classification records, labeling practices, and access control lists for confidential information. Provide procedure for classifying, labeling, and handling confidential information.", - "frequency": "yearly", - "department": "gov" + "evidenceId": "data_classification_records" } ] }, @@ -967,17 +749,12 @@ { "id": "C1.2-policy", "type": "policy", - "description": "Reference to the Data Classification Policy which includes controls for restricting access to confidential information.", - "policyId": "data_classification", - "frequency": "yearly" + "policyId": "data_classification" }, { "id": "C1.2-evidence", "type": "evidence", - "name": "Access Logs", - "description": "Access logs, periodic access reviews, and certification records. Provide procedure for granting, monitoring, and revoking access to confidential data based on need-to-know.", - "frequency": "quarterly", - "department": "it" + "evidenceId": "access_logs" } ] }, @@ -991,17 +768,12 @@ { "id": "C1.3-policy", "type": "policy", - "description": "Reference to the Data Classification Policy which includes provisions for secure data disposal.", - "policyId": "data_classification", - "frequency": "yearly" + "policyId": "data_classification" }, { "id": "C1.3-evidence", "type": "evidence", - "name": "Disposal Records", - "description": "Disposal records, certificates of destruction, and audit logs. Provide procedure for secure data destruction and disposal (electronic and physical).", - "frequency": "yearly", - "department": "it" + "evidenceId": "disposal_records" } ] }, @@ -1015,17 +787,12 @@ { "id": "PI1.1-policy", "type": "policy", - "description": "Reference to the Information Security Policy that addresses data accuracy and completeness.", - "policyId": "information_security", - "frequency": "yearly" + "policyId": "information_security" }, { "id": "PI1.1-evidence", "type": "evidence", - "name": "Data Validation Records", - "description": "Data validation reports, exception logs, and audit records. Provide procedure for validating data inputs and outputs to ensure completeness and accuracy.", - "frequency": "quarterly", - "department": "it" + "evidenceId": "data_validation_records" } ] }, @@ -1039,17 +806,12 @@ { "id": "PI1.2-policy", "type": "policy", - "description": "Reference to the Information Security Policy that outlines controls over data processing.", - "policyId": "information_security", - "frequency": "yearly" + "policyId": "information_security" }, { "id": "PI1.2-evidence", "type": "evidence", - "name": "Data Processing Logs", - "description": "Data processing logs, validation reports, and exception handling records. Provide procedure for ensuring proper validation, error handling, and reconciliation during data processing.", - "frequency": "quarterly", - "department": "it" + "evidenceId": "data_processing_logs" } ] }, @@ -1063,17 +825,12 @@ { "id": "PI1.3-policy", "type": "policy", - "description": "Reference to the Information Security Policy that outlines procedures for handling processing exceptions.", - "policyId": "information_security", - "frequency": "yearly" + "policyId": "information_security" }, { "id": "PI1.3-evidence", "type": "evidence", - "name": "Exception Logs", - "description": "Exception logs, resolution documentation, and process improvement records. Provide procedure for detecting exceptions, escalating issues, and documenting resolutions.", - "frequency": "quarterly", - "department": "itsm" + "evidenceId": "exception_logs" } ] }, @@ -1087,17 +844,12 @@ { "id": "P1.1-policy", "type": "policy", - "description": "Reference to the Privacy Policy that informs individuals about personal data collection, usage, and disclosure practices.", - "policyId": "privacy", - "frequency": "yearly" + "policyId": "privacy" }, { "id": "P1.1-evidence", "type": "evidence", - "name": "Privacy Notice", - "description": "Copies of the privacy notice, version history, and distribution logs. Provide procedure for distributing and updating the privacy notice.", - "frequency": "yearly", - "department": "gov" + "evidenceId": "privacy_notice" } ] }, @@ -1111,17 +863,12 @@ { "id": "P1.2-policy", "type": "policy", - "description": "Reference to the Privacy Policy that requires explicit consent for the collection and processing of personal data.", - "policyId": "privacy", - "frequency": "yearly" + "policyId": "privacy" }, { "id": "P1.2-evidence", "type": "evidence", - "name": "Consent Records", - "description": "Consent records, opt-in logs, and audit trails for consent management. Provide procedure for obtaining, recording, and managing individual consent.", - "frequency": "yearly", - "department": "gov" + "evidenceId": "consent_records" } ] }, @@ -1135,17 +882,12 @@ { "id": "P1.3-policy", "type": "policy", - "description": "Reference to the Privacy Policy that defines retention periods and secure disposal methods for personal data.", - "policyId": "privacy", - "frequency": "yearly" + "policyId": "privacy" }, { "id": "P1.3-evidence", "type": "evidence", - "name": "Retention Schedules", - "description": "Retention schedules, disposal logs, and certificates of data destruction. Provide procedure for reviewing, archiving, and securely disposing of personal data.", - "frequency": "yearly", - "department": "gov" + "evidenceId": "retention_schedules" } ] } diff --git a/packages/data/evidence/access_control_records.json b/packages/data/evidence/access_control_records.json new file mode 100644 index 0000000000..1abfdb9970 --- /dev/null +++ b/packages/data/evidence/access_control_records.json @@ -0,0 +1,7 @@ +{ + "id": "access_control_records", + "name": "Access Control Records", + "description": "Access control configurations, firewall logs, and system access review reports. Provide Access Management Procedures document that outlines granting, monitoring, and revoking system access including access logging and periodic reviews.", + "frequency": "quarterly", + "department": "it" +} diff --git a/packages/data/evidence/access_logs.json b/packages/data/evidence/access_logs.json new file mode 100644 index 0000000000..13afd83eae --- /dev/null +++ b/packages/data/evidence/access_logs.json @@ -0,0 +1,7 @@ +{ + "id": "access_logs", + "name": "Access Logs", + "description": "Access logs, periodic access reviews, and certification records. Provide procedure for granting, monitoring, and revoking access to confidential data based on need-to-know.", + "frequency": "quarterly", + "department": "it" +} diff --git a/packages/data/evidence/access_removal_records.json b/packages/data/evidence/access_removal_records.json new file mode 100644 index 0000000000..e7aa092a28 --- /dev/null +++ b/packages/data/evidence/access_removal_records.json @@ -0,0 +1,7 @@ +{ + "id": "access_removal_records", + "name": "Access Removal Records", + "description": "User termination logs, de-provisioning records, and access review reports. Provide Access Removal Procedures document that outlines de-provisioning user access immediately after termination or role change.", + "frequency": "quarterly", + "department": "it" +} diff --git a/packages/data/evidence/access_review_records.json b/packages/data/evidence/access_review_records.json new file mode 100644 index 0000000000..e5cf82e339 --- /dev/null +++ b/packages/data/evidence/access_review_records.json @@ -0,0 +1,7 @@ +{ + "id": "access_review_records", + "name": "Access Review Records", + "description": "Access review logs, user access reports, and management sign-off documentation. Provide Access Review Procedures document that outlines conducting regular access reviews and validating that user permissions align with current roles.", + "frequency": "quarterly", + "department": "it" +} diff --git a/packages/data/evidence/account_management_records.json b/packages/data/evidence/account_management_records.json new file mode 100644 index 0000000000..965e4a55e0 --- /dev/null +++ b/packages/data/evidence/account_management_records.json @@ -0,0 +1,7 @@ +{ + "id": "account_management_records", + "name": "Account Management Records", + "description": "Account provisioning logs, privileged account review reports, and system audit logs. Provide Account Management Procedures document that outlines provisioning system accounts, managing privileged access, and monitoring account activity.", + "frequency": "quarterly", + "department": "it" +} diff --git a/packages/data/evidence/authentication_records.json b/packages/data/evidence/authentication_records.json new file mode 100644 index 0000000000..7d0d719e6f --- /dev/null +++ b/packages/data/evidence/authentication_records.json @@ -0,0 +1,7 @@ +{ + "id": "authentication_records", + "name": "Authentication Records", + "description": "Authentication logs, MFA configuration records, and user account management records. Provide Authentication Management Procedures document that outlines user registration, credential issuance, and ongoing authentication management.", + "frequency": "quarterly", + "department": "it" +} diff --git a/packages/data/evidence/board_meeting_documentation.json b/packages/data/evidence/board_meeting_documentation.json new file mode 100644 index 0000000000..4da95fc59b --- /dev/null +++ b/packages/data/evidence/board_meeting_documentation.json @@ -0,0 +1,7 @@ +{ + "id": "board_meeting_documentation", + "name": "Board Meeting Documentation", + "description": "Minutes of board meetings and oversight reports demonstrating active review of internal controls. Provide Board Oversight Procedures document that outlines scheduled reviews, risk assessments, and communication channels with management.", + "frequency": "quarterly", + "department": "admin" +} diff --git a/packages/data/evidence/business_continuity_and_disaster_recovery_testing_records.json b/packages/data/evidence/business_continuity_and_disaster_recovery_testing_records.json new file mode 100644 index 0000000000..d5c52aff74 --- /dev/null +++ b/packages/data/evidence/business_continuity_and_disaster_recovery_testing_records.json @@ -0,0 +1,7 @@ +{ + "id": "business_continuity_and_disaster_recovery_testing_records", + "name": "Business Continuity and Disaster Recovery Testing Records", + "description": "Test reports, remediation logs, and updated BC/DR plans. Provide procedure for conducting regular BC/DR tests and documenting test results with follow-up actions.", + "frequency": "yearly", + "department": "it" +} diff --git a/packages/data/evidence/business_continuity_plans.json b/packages/data/evidence/business_continuity_plans.json new file mode 100644 index 0000000000..479a2a43b8 --- /dev/null +++ b/packages/data/evidence/business_continuity_plans.json @@ -0,0 +1,7 @@ +{ + "id": "business_continuity_plans", + "name": "Business Continuity Plans", + "description": "Business continuity plans, BIA reports, and disaster recovery test results. Provide procedure for conducting business impact analyses, backup operations, and disaster recovery testing.", + "frequency": "yearly", + "department": "it" +} diff --git a/packages/data/evidence/capacity_reports.json b/packages/data/evidence/capacity_reports.json new file mode 100644 index 0000000000..9dc6464d9f --- /dev/null +++ b/packages/data/evidence/capacity_reports.json @@ -0,0 +1,7 @@ +{ + "id": "capacity_reports", + "name": "Capacity Reports", + "description": "Capacity reports, trend analysis, and resource utilization logs. Provide procedure for capacity analysis, resource allocation, and performance tuning.", + "frequency": "quarterly", + "department": "it" +} diff --git a/packages/data/evidence/change_management_records.json b/packages/data/evidence/change_management_records.json new file mode 100644 index 0000000000..0b30fb6826 --- /dev/null +++ b/packages/data/evidence/change_management_records.json @@ -0,0 +1,7 @@ +{ + "id": "change_management_records", + "name": "Change Management Records", + "description": "Change logs, approval records, and configuration management documentation. Provide Change Management Procedures document that outlines managing, reviewing, and approving changes to information assets, including rollback measures.", + "frequency": "yearly", + "department": "it" +} diff --git a/packages/data/evidence/change_request_logs.json b/packages/data/evidence/change_request_logs.json new file mode 100644 index 0000000000..59acb381bb --- /dev/null +++ b/packages/data/evidence/change_request_logs.json @@ -0,0 +1,7 @@ +{ + "id": "change_request_logs", + "name": "Change Request Logs", + "description": "Change request logs, approval records, and post-change review reports. Provide procedure for submitting, reviewing, and approving changes, including emergency change processes.", + "frequency": "yearly", + "department": "itsm" +} diff --git a/packages/data/evidence/change_risk_documentation.json b/packages/data/evidence/change_risk_documentation.json new file mode 100644 index 0000000000..f7a2bbf151 --- /dev/null +++ b/packages/data/evidence/change_risk_documentation.json @@ -0,0 +1,7 @@ +{ + "id": "change_risk_documentation", + "name": "Change Risk Documentation", + "description": "Change impact assessments, risk logs, and management approval records. Provide Change Risk Assessment Procedures document that outlines assessing the impact of changes and planning mitigations for associated risks.", + "frequency": "yearly", + "department": "it" +} diff --git a/packages/data/evidence/communication_records.json b/packages/data/evidence/communication_records.json new file mode 100644 index 0000000000..aae5840d23 --- /dev/null +++ b/packages/data/evidence/communication_records.json @@ -0,0 +1,7 @@ +{ + "id": "communication_records", + "name": "Communication Records", + "description": "Communication logs, email distributions, and meeting minutes. Provide Internal Communication Procedures document that outlines internal reporting, announcements, and feedback regarding internal controls.", + "frequency": "quarterly", + "department": "admin" +} diff --git a/packages/data/evidence/consent_records.json b/packages/data/evidence/consent_records.json new file mode 100644 index 0000000000..3f57300f9e --- /dev/null +++ b/packages/data/evidence/consent_records.json @@ -0,0 +1,7 @@ +{ + "id": "consent_records", + "name": "Consent Records", + "description": "Consent records, opt-in logs, and audit trails for consent management. Provide procedure for obtaining, recording, and managing individual consent.", + "frequency": "yearly", + "department": "gov" +} diff --git a/packages/data/evidence/control_implementation_records.json b/packages/data/evidence/control_implementation_records.json new file mode 100644 index 0000000000..326bb7b7c6 --- /dev/null +++ b/packages/data/evidence/control_implementation_records.json @@ -0,0 +1,7 @@ +{ + "id": "control_implementation_records", + "name": "Control Implementation Records", + "description": "Control design documents, implementation records, and risk mitigation assessments. Provide Control Implementation Procedures document that outlines developing, documenting, and implementing controls to mitigate risks.", + "frequency": "yearly", + "department": "gov" +} diff --git a/packages/data/evidence/control_testing_documentation.json b/packages/data/evidence/control_testing_documentation.json new file mode 100644 index 0000000000..ad1b9bc504 --- /dev/null +++ b/packages/data/evidence/control_testing_documentation.json @@ -0,0 +1,7 @@ +{ + "id": "control_testing_documentation", + "name": "Control Testing Documentation", + "description": "Internal audit reports, control testing records, and management review documentation. Provide Control Testing Procedures document that outlines ongoing and periodic testing of internal controls.", + "frequency": "quarterly", + "department": "gov" +} diff --git a/packages/data/evidence/data_classification_records.json b/packages/data/evidence/data_classification_records.json new file mode 100644 index 0000000000..a93fde209f --- /dev/null +++ b/packages/data/evidence/data_classification_records.json @@ -0,0 +1,7 @@ +{ + "id": "data_classification_records", + "name": "Data Classification Records", + "description": "Data classification records, labeling practices, and access control lists for confidential information. Provide procedure for classifying, labeling, and handling confidential information.", + "frequency": "yearly", + "department": "gov" +} diff --git a/packages/data/evidence/data_processing_logs.json b/packages/data/evidence/data_processing_logs.json new file mode 100644 index 0000000000..19b28899a4 --- /dev/null +++ b/packages/data/evidence/data_processing_logs.json @@ -0,0 +1,7 @@ +{ + "id": "data_processing_logs", + "name": "Data Processing Logs", + "description": "Data processing logs, validation reports, and exception handling records. Provide procedure for ensuring proper validation, error handling, and reconciliation during data processing.", + "frequency": "quarterly", + "department": "it" +} diff --git a/packages/data/evidence/data_quality_documentation.json b/packages/data/evidence/data_quality_documentation.json new file mode 100644 index 0000000000..08b86a65fb --- /dev/null +++ b/packages/data/evidence/data_quality_documentation.json @@ -0,0 +1,7 @@ +{ + "id": "data_quality_documentation", + "name": "Data Quality Documentation", + "description": "Data quality reports, audit logs, and records of corrective actions. Provide Data Quality Procedures document that outlines validating data inputs and correcting errors throughout data processing.", + "frequency": "quarterly", + "department": "it" +} diff --git a/packages/data/evidence/data_validation_records.json b/packages/data/evidence/data_validation_records.json new file mode 100644 index 0000000000..95319c3e64 --- /dev/null +++ b/packages/data/evidence/data_validation_records.json @@ -0,0 +1,7 @@ +{ + "id": "data_validation_records", + "name": "Data Validation Records", + "description": "Data validation reports, exception logs, and audit records. Provide procedure for validating data inputs and outputs to ensure completeness and accuracy.", + "frequency": "quarterly", + "department": "it" +} diff --git a/packages/data/evidence/deficiency_management_records.json b/packages/data/evidence/deficiency_management_records.json new file mode 100644 index 0000000000..24fdc18e2e --- /dev/null +++ b/packages/data/evidence/deficiency_management_records.json @@ -0,0 +1,7 @@ +{ + "id": "deficiency_management_records", + "name": "Deficiency Management Records", + "description": "Deficiency logs, remediation plans, and follow-up audit reports. Provide Deficiency Management Procedures document that outlines documenting, tracking, and remediating control deficiencies.", + "frequency": "quarterly", + "department": "gov" +} diff --git a/packages/data/evidence/disposal_records.json b/packages/data/evidence/disposal_records.json new file mode 100644 index 0000000000..724b790422 --- /dev/null +++ b/packages/data/evidence/disposal_records.json @@ -0,0 +1,7 @@ +{ + "id": "disposal_records", + "name": "Disposal Records", + "description": "Disposal records, certificates of destruction, and audit logs. Provide procedure for secure data destruction and disposal (electronic and physical).", + "frequency": "yearly", + "department": "it" +} diff --git a/packages/data/evidence/ethics_compliance_documentation.json b/packages/data/evidence/ethics_compliance_documentation.json new file mode 100644 index 0000000000..313a7c0104 --- /dev/null +++ b/packages/data/evidence/ethics_compliance_documentation.json @@ -0,0 +1,7 @@ +{ + "id": "ethics_compliance_documentation", + "name": "Ethics Compliance Documentation", + "description": "Signed acknowledgment forms, training completion records, and records of investigations or disciplinary actions. Provide Ethics Compliance Procedures document that outlines reporting, investigating, and addressing breaches of the code.", + "frequency": "yearly", + "department": "hr" +} diff --git a/packages/data/evidence/exception_logs.json b/packages/data/evidence/exception_logs.json new file mode 100644 index 0000000000..6dd4c2906e --- /dev/null +++ b/packages/data/evidence/exception_logs.json @@ -0,0 +1,7 @@ +{ + "id": "exception_logs", + "name": "Exception Logs", + "description": "Exception logs, resolution documentation, and process improvement records. Provide procedure for detecting exceptions, escalating issues, and documenting resolutions.", + "frequency": "quarterly", + "department": "itsm" +} diff --git a/packages/data/evidence/external_communication_records.json b/packages/data/evidence/external_communication_records.json new file mode 100644 index 0000000000..6b582ad41e --- /dev/null +++ b/packages/data/evidence/external_communication_records.json @@ -0,0 +1,7 @@ +{ + "id": "external_communication_records", + "name": "External Communication Records", + "description": "Records of external communications, press releases, and stakeholder correspondence. Provide External Communication Procedures document that outlines drafting, approving, and disseminating external communications related to internal controls.", + "frequency": "yearly", + "department": "admin" +} diff --git a/packages/data/evidence/fraud_risk_documentation.json b/packages/data/evidence/fraud_risk_documentation.json new file mode 100644 index 0000000000..1ec190346e --- /dev/null +++ b/packages/data/evidence/fraud_risk_documentation.json @@ -0,0 +1,7 @@ +{ + "id": "fraud_risk_documentation", + "name": "Fraud Risk Documentation", + "description": "Fraud risk assessment reports, internal audit findings, and remediation tracking records. Provide Fraud Risk Assessment Procedures document that outlines conducting fraud risk assessments and reporting findings.", + "frequency": "yearly", + "department": "gov" +} diff --git a/packages/data/evidence/hr_documentation.json b/packages/data/evidence/hr_documentation.json new file mode 100644 index 0000000000..de5de61a51 --- /dev/null +++ b/packages/data/evidence/hr_documentation.json @@ -0,0 +1,7 @@ +{ + "id": "hr_documentation", + "name": "HR Documentation", + "description": "HR records, training logs, and performance evaluations. Provide Talent Management Procedures document that outlines talent acquisition, performance management, and professional development.", + "frequency": "yearly", + "department": "hr" +} diff --git a/packages/data/evidence/incident_analysis_records.json b/packages/data/evidence/incident_analysis_records.json new file mode 100644 index 0000000000..b8b7dde8d2 --- /dev/null +++ b/packages/data/evidence/incident_analysis_records.json @@ -0,0 +1,7 @@ +{ + "id": "incident_analysis_records", + "name": "Incident Analysis Records", + "description": "Incident analysis reports, lessons learned documentation, and remediation tracking records. Provide procedures document that outlines conducting root cause analysis and developing remediation plans for recurring issues.", + "frequency": "yearly", + "department": "it" +} diff --git a/packages/data/evidence/incident_communication_records.json b/packages/data/evidence/incident_communication_records.json new file mode 100644 index 0000000000..f3e0225fc5 --- /dev/null +++ b/packages/data/evidence/incident_communication_records.json @@ -0,0 +1,7 @@ +{ + "id": "incident_communication_records", + "name": "Incident Communication Records", + "description": "Communication logs, notifications, and records of stakeholder communications during incidents. Provide procedures document that outlines timely communication of security incidents to stakeholders, regulators, and internal teams.", + "frequency": "yearly", + "department": "it" +} diff --git a/packages/data/evidence/incident_recovery_records.json b/packages/data/evidence/incident_recovery_records.json new file mode 100644 index 0000000000..5b5d5da768 --- /dev/null +++ b/packages/data/evidence/incident_recovery_records.json @@ -0,0 +1,7 @@ +{ + "id": "incident_recovery_records", + "name": "Incident Recovery Records", + "description": "Incident recovery test results, post-incident reviews, and restoration logs. Provide procedure for incident response, recovery, and restoration of systems after an outage.", + "frequency": "yearly", + "department": "it" +} diff --git a/packages/data/evidence/incident_response_records.json b/packages/data/evidence/incident_response_records.json new file mode 100644 index 0000000000..465f53bfd4 --- /dev/null +++ b/packages/data/evidence/incident_response_records.json @@ -0,0 +1,7 @@ +{ + "id": "incident_response_records", + "name": "Incident Response Records", + "description": "Incident logs, response drill reports, and post-incident review documentation. Provide Security Incident Response Procedures document that outlines detecting, containing, eradicating, and recovering from security incidents.", + "frequency": "yearly", + "department": "it" +} diff --git a/packages/data/evidence/infrastructure_monitoring_records.json b/packages/data/evidence/infrastructure_monitoring_records.json new file mode 100644 index 0000000000..03b9853e09 --- /dev/null +++ b/packages/data/evidence/infrastructure_monitoring_records.json @@ -0,0 +1,7 @@ +{ + "id": "infrastructure_monitoring_records", + "name": "Infrastructure Monitoring Records", + "description": "Monitoring tool reports, alert logs, and incident tracking records. Provide Infrastructure Monitoring Procedures document that outlines real-time monitoring of infrastructure performance, security events, and system vulnerabilities.", + "frequency": "quarterly", + "department": "it" +} diff --git a/packages/data/evidence/malware_prevention_records.json b/packages/data/evidence/malware_prevention_records.json new file mode 100644 index 0000000000..cf37c6ef68 --- /dev/null +++ b/packages/data/evidence/malware_prevention_records.json @@ -0,0 +1,7 @@ +{ + "id": "malware_prevention_records", + "name": "Malware Prevention Records", + "description": "Antivirus logs, malware detection alerts, and remediation records. Provide Malware Prevention Procedures document that outlines scanning, detecting, and responding to malware incidents on endpoints and networks.", + "frequency": "quarterly", + "department": "it" +} diff --git a/packages/data/evidence/management_structure_documentation.json b/packages/data/evidence/management_structure_documentation.json new file mode 100644 index 0000000000..7fab52beb1 --- /dev/null +++ b/packages/data/evidence/management_structure_documentation.json @@ -0,0 +1,7 @@ +{ + "id": "management_structure_documentation", + "name": "Management Structure Documentation", + "description": "Organizational charts, management meeting minutes, and training records. Provide Management Oversight Procedures document that outlines decision-making processes and periodic reporting.", + "frequency": "yearly", + "department": "admin" +} diff --git a/packages/data/evidence/personnel_compliance_documentation.json b/packages/data/evidence/personnel_compliance_documentation.json new file mode 100644 index 0000000000..4127cbf584 --- /dev/null +++ b/packages/data/evidence/personnel_compliance_documentation.json @@ -0,0 +1,7 @@ +{ + "id": "personnel_compliance_documentation", + "name": "Personnel Compliance Documentation", + "description": "Employee acknowledgment forms, training records, and disciplinary documentation. Provide Personnel Accountability Procedures document that outlines monitoring, enforcing, and reviewing personnel accountability.", + "frequency": "yearly", + "department": "hr" +} diff --git a/packages/data/evidence/physical_access_records.json b/packages/data/evidence/physical_access_records.json new file mode 100644 index 0000000000..aac5fec3d3 --- /dev/null +++ b/packages/data/evidence/physical_access_records.json @@ -0,0 +1,7 @@ +{ + "id": "physical_access_records", + "name": "Physical Access Records", + "description": "Facility access logs, visitor sign-in records, and security camera reports. Provide Physical Access Procedures document that outlines issuing physical access credentials, managing visitor access, and monitoring facility entry.", + "frequency": "quarterly", + "department": "admin" +} diff --git a/packages/data/evidence/policy_implementation_records.json b/packages/data/evidence/policy_implementation_records.json new file mode 100644 index 0000000000..27814eb18d --- /dev/null +++ b/packages/data/evidence/policy_implementation_records.json @@ -0,0 +1,7 @@ +{ + "id": "policy_implementation_records", + "name": "Policy Implementation Records", + "description": "Policy distribution records, training logs, and compliance monitoring reports. Provide Policy Implementation Procedures document that outlines operationalizing policies including documentation, training, and compliance monitoring.", + "frequency": "yearly", + "department": "gov" +} diff --git a/packages/data/evidence/privacy_notice.json b/packages/data/evidence/privacy_notice.json new file mode 100644 index 0000000000..14b430f296 --- /dev/null +++ b/packages/data/evidence/privacy_notice.json @@ -0,0 +1,7 @@ +{ + "id": "privacy_notice", + "name": "Privacy Notice", + "description": "Copies of the privacy notice, version history, and distribution logs. Provide procedure for distributing and updating the privacy notice.", + "frequency": "yearly", + "department": "gov" +} diff --git a/packages/data/evidence/recovery_records.json b/packages/data/evidence/recovery_records.json new file mode 100644 index 0000000000..a0e3aff98f --- /dev/null +++ b/packages/data/evidence/recovery_records.json @@ -0,0 +1,7 @@ +{ + "id": "recovery_records", + "name": "Recovery Records", + "description": "Recovery test results, restoration logs, and incident recovery reports. Provide Recovery Procedures document that outlines restoring systems and data following a security incident, including backup restoration and validation.", + "frequency": "yearly", + "department": "it" +} diff --git a/packages/data/evidence/retention_schedules.json b/packages/data/evidence/retention_schedules.json new file mode 100644 index 0000000000..ad16f0ee48 --- /dev/null +++ b/packages/data/evidence/retention_schedules.json @@ -0,0 +1,7 @@ +{ + "id": "retention_schedules", + "name": "Retention Schedules", + "description": "Retention schedules, disposal logs, and certificates of data destruction. Provide procedure for reviewing, archiving, and securely disposing of personal data.", + "frequency": "yearly", + "department": "gov" +} diff --git a/packages/data/evidence/risk_assessment_documentation.json b/packages/data/evidence/risk_assessment_documentation.json new file mode 100644 index 0000000000..6d8504d1e7 --- /dev/null +++ b/packages/data/evidence/risk_assessment_documentation.json @@ -0,0 +1,7 @@ +{ + "id": "risk_assessment_documentation", + "name": "Risk Assessment Documentation", + "description": "Risk assessment reports, risk registers, and management review minutes. Provide Risk Assessment Procedures document that outlines conducting regular risk assessments, documenting risks, and assigning risk owners.", + "frequency": "yearly", + "department": "gov" +} diff --git a/packages/data/evidence/risk_identification_records.json b/packages/data/evidence/risk_identification_records.json new file mode 100644 index 0000000000..06f082c2e4 --- /dev/null +++ b/packages/data/evidence/risk_identification_records.json @@ -0,0 +1,7 @@ +{ + "id": "risk_identification_records", + "name": "Risk Identification Records", + "description": "Risk register entries, workshop records, and risk analysis documentation. Provide Risk Identification Procedures document that outlines risk identification using workshops, surveys, and data analysis.", + "frequency": "yearly", + "department": "gov" +} diff --git a/packages/data/evidence/technology_control_records.json b/packages/data/evidence/technology_control_records.json new file mode 100644 index 0000000000..a04b496f11 --- /dev/null +++ b/packages/data/evidence/technology_control_records.json @@ -0,0 +1,7 @@ +{ + "id": "technology_control_records", + "name": "Technology Control Records", + "description": "System configuration records, monitoring reports, and technology audit logs. Provide Technology Control Procedures document that outlines implementing and monitoring technology controls such as firewalls, antivirus, and system hardening.", + "frequency": "quarterly", + "department": "it" +} diff --git a/packages/data/evidence/uptime_reports.json b/packages/data/evidence/uptime_reports.json new file mode 100644 index 0000000000..8b1f9ffbcd --- /dev/null +++ b/packages/data/evidence/uptime_reports.json @@ -0,0 +1,7 @@ +{ + "id": "uptime_reports", + "name": "Uptime Reports", + "description": "Uptime reports, incident logs, and SLA monitoring records. Provide procedure for monitoring system availability, reporting outages, and ensuring continuity of operations.", + "frequency": "quarterly", + "department": "itsm" +} diff --git a/packages/data/evidence/vendor_risk_assessment_records.json b/packages/data/evidence/vendor_risk_assessment_records.json new file mode 100644 index 0000000000..ee502dbec9 --- /dev/null +++ b/packages/data/evidence/vendor_risk_assessment_records.json @@ -0,0 +1,7 @@ +{ + "id": "vendor_risk_assessment_records", + "name": "Vendor Risk Assessment Records", + "description": "Vendor risk assessment reports, due diligence records, and contract reviews. Provide procedure for assessing vendor risk through questionnaires, audits, and continuous monitoring.", + "frequency": "yearly", + "department": "gov" +} diff --git a/packages/data/policies/access_control.json b/packages/data/policies/access_control.json index 139fd6157c..65719e5f9f 100644 --- a/packages/data/policies/access_control.json +++ b/packages/data/policies/access_control.json @@ -6,6 +6,7 @@ "name": "Access Control Policy", "description": "This policy defines the requirements for granting, monitoring, and revoking access to the organization’s information systems and data based on the principle of least privilege.", "frequency": "yearly", + "department": "it", "usedBy": { "soc2": ["CC6.1", "CC6.2", "CC6.3", "CC6.4", "CC6.5", "CC6.6"] } diff --git a/packages/data/policies/application_security.json b/packages/data/policies/application_security.json index 448feeb92c..ba11d3057e 100644 --- a/packages/data/policies/application_security.json +++ b/packages/data/policies/application_security.json @@ -6,6 +6,7 @@ "name": "Application Security Policy", "description": "This policy outlines the security framework and requirements for applications, notably web applications, within the organization's production environment.", "frequency": "yearly", + "department": "it", "usedBy": { "soc2": ["CC7.1", "CC7.2", "CC7.4"] } diff --git a/packages/data/policies/availability.json b/packages/data/policies/availability.json index b3b54a632f..1abde50aae 100644 --- a/packages/data/policies/availability.json +++ b/packages/data/policies/availability.json @@ -6,6 +6,7 @@ "name": "Availability Policy", "description": "This policy outlines the requirements for proper controls to protect the availability of the organization's information systems.", "frequency": "yearly", + "department": "it", "usedBy": { "soc2": ["CC9.1", "CC7.3", "CC7.5", "A1.1", "A1.2"] } diff --git a/packages/data/policies/business_continuity.json b/packages/data/policies/business_continuity.json index 45dda43c35..e3a496c98f 100644 --- a/packages/data/policies/business_continuity.json +++ b/packages/data/policies/business_continuity.json @@ -6,6 +6,7 @@ "name": "Business Continuity & Disaster Recovery Policy", "description": "This policy outlines the strategies and procedures for ensuring the availability of critical systems and data during and after a disruptive event.", "frequency": "yearly", + "department": "it", "usedBy": { "soc2": ["CC7.3", "A1.3", "CC9.1", "CC9.9"] } diff --git a/packages/data/policies/change_management.json b/packages/data/policies/change_management.json index f8fab5bb09..38f67b7246 100644 --- a/packages/data/policies/change_management.json +++ b/packages/data/policies/change_management.json @@ -6,6 +6,7 @@ "name": "Change Management Policy", "description": "This policy defines the process for requesting, reviewing, approving, and documenting changes to the organization's information systems and infrastructure.", "frequency": "yearly", + "department": "it", "usedBy": { "soc2": ["CC3.4", "CC8.1", "CC6.7"] } diff --git a/packages/data/policies/classification.json b/packages/data/policies/classification.json index a85a96ccd2..9c01d8282d 100644 --- a/packages/data/policies/classification.json +++ b/packages/data/policies/classification.json @@ -6,6 +6,7 @@ "name": "Data Classification Policy", "description": "This policy outlines the requirements for data classification.", "frequency": "yearly", + "department": "gov", "usedBy": { "soc2": ["CC6.1", "CC8.1", "CC6.6"] } diff --git a/packages/data/policies/code_of_conduct.json b/packages/data/policies/code_of_conduct.json index b7a1031c70..2aedfe549a 100644 --- a/packages/data/policies/code_of_conduct.json +++ b/packages/data/policies/code_of_conduct.json @@ -6,6 +6,7 @@ "name": "Code of Conduct Policy", "description": "This policy outlines the expected behavior from employees towards their colleagues, supervisors, and the organization as a whole.", "frequency": "yearly", + "department": "hr", "usedBy": { "soc2": ["CC1.1", "CC6.1"] } diff --git a/packages/data/policies/confidentiality.json b/packages/data/policies/confidentiality.json index 0eb842b7ff..20c453d732 100644 --- a/packages/data/policies/confidentiality.json +++ b/packages/data/policies/confidentiality.json @@ -6,6 +6,7 @@ "name": "Confidentiality Policy", "description": "This policy outlines the requirements for maintaining the confidentiality of sensitive and proprietary information within the organization.", "frequency": "yearly", + "department": "gov", "usedBy": { "soc2": ["CC9.9", "CC6.1"] } diff --git a/packages/data/policies/corporate_governance.json b/packages/data/policies/corporate_governance.json index d204200f26..dc0374cc84 100644 --- a/packages/data/policies/corporate_governance.json +++ b/packages/data/policies/corporate_governance.json @@ -6,6 +6,7 @@ "name": "Corporate Governance Policy", "description": "This policy defines the overall governance framework including board oversight, management responsibilities, and organizational structure to ensure effective oversight and accountability.", "frequency": "yearly", + "department": "admin", "usedBy": { "soc2": ["CC1.1", "CC1.2", "CC1.5", "CC2.2", "CC2.3"] } diff --git a/packages/data/policies/cyber_risk.json b/packages/data/policies/cyber_risk.json index a67274f093..09d7e40906 100644 --- a/packages/data/policies/cyber_risk.json +++ b/packages/data/policies/cyber_risk.json @@ -6,6 +6,7 @@ "name": "Cyber Risk Assessment Policy", "description": "This policy outlines the requirements for conducting cyber risk assessments to identify, evaluate, and mitigate cybersecurity threats to the organization.", "frequency": "yearly", + "department": "it", "usedBy": { "soc2": ["CC1.1", "CC1.2", "CC1.3", "CC1.4", "CC1.5"] } diff --git a/packages/data/policies/data_center.json b/packages/data/policies/data_center.json index e2d37841a4..4444c3ce71 100644 --- a/packages/data/policies/data_center.json +++ b/packages/data/policies/data_center.json @@ -6,6 +6,7 @@ "name": "Data Center Policy", "description": "This policy outlines the requirements for the organization's data center facilities to ensure protection, availability, and reliability of critical systems and data.", "frequency": "yearly", + "department": "it", "usedBy": { "soc2": ["CC6.1", "CC6.2", "CC8.1", "CC7.1"] } diff --git a/packages/data/policies/data_classification.json b/packages/data/policies/data_classification.json index 70aa0f1c66..b63b23e0f6 100644 --- a/packages/data/policies/data_classification.json +++ b/packages/data/policies/data_classification.json @@ -6,6 +6,7 @@ "name": "Data Classification Policy", "description": "This policy establishes a framework for classifying data based on sensitivity and defines handling requirements for each classification level.", "frequency": "yearly", + "department": "gov", "usedBy": { "soc2": ["C1.1", "C1.2", "C1.3"] } diff --git a/packages/data/policies/disaster_recovery.json b/packages/data/policies/disaster_recovery.json index 2b96cd28e9..c13156ff6a 100644 --- a/packages/data/policies/disaster_recovery.json +++ b/packages/data/policies/disaster_recovery.json @@ -6,6 +6,7 @@ "name": "Disaster Recovery Policy", "description": "This policy outlines the requirements for disaster recovery planning to ensure that critical business operations can be resumed in the event of a disruption.", "frequency": "yearly", + "department": "it", "usedBy": { "soc2": ["CC9.1", "CC8.1"] } diff --git a/packages/data/policies/human_resources.json b/packages/data/policies/human_resources.json index 0feb351186..24a05bd9ae 100644 --- a/packages/data/policies/human_resources.json +++ b/packages/data/policies/human_resources.json @@ -6,6 +6,7 @@ "name": "Human Resources Policy", "description": "This policy outlines the principles and practices for recruitment, employee management, performance evaluations, and the enforcement of internal control responsibilities.", "frequency": "yearly", + "department": "hr", "usedBy": { "soc2": ["CC1.3", "CC1.4"] } diff --git a/packages/data/policies/incident_response.json b/packages/data/policies/incident_response.json index f384e2e7f5..b89422d0a0 100644 --- a/packages/data/policies/incident_response.json +++ b/packages/data/policies/incident_response.json @@ -6,6 +6,7 @@ "name": "Incident Response Policy", "description": "This policy establishes the framework and procedures for detecting, responding to, and recovering from security incidents.", "frequency": "yearly", + "department": "it", "usedBy": { "soc2": ["CC7.2", "CC7.4", "CC7.5"] } diff --git a/packages/data/policies/information_security.json b/packages/data/policies/information_security.json index 60421a068c..84e3fab2f5 100644 --- a/packages/data/policies/information_security.json +++ b/packages/data/policies/information_security.json @@ -6,6 +6,7 @@ "name": "Information Security Policy", "description": "This policy establishes the framework for protecting the organization's information assets by defining security objectives, roles, responsibilities, and controls.", "frequency": "yearly", + "department": "it", "usedBy": { "soc2": ["CC2.1", "PI1.1", "PI1.2", "PI1.3", "CC5.2"] } diff --git a/packages/data/policies/password_policy.json b/packages/data/policies/password_policy.json index e541449d1c..2cc8482443 100644 --- a/packages/data/policies/password_policy.json +++ b/packages/data/policies/password_policy.json @@ -5,6 +5,7 @@ "name": "Password Policy", "description": "This policy outlines the requirements for passwords used by employees.", "frequency": "yearly", + "department": "it", "usedBy": { "soc2": ["CC1.1", "CC1.2", "CC1.3"] } diff --git a/packages/data/policies/privacy.json b/packages/data/policies/privacy.json index 032c13f392..ed77b92d28 100644 --- a/packages/data/policies/privacy.json +++ b/packages/data/policies/privacy.json @@ -6,6 +6,7 @@ "name": "Privacy Policy", "description": "This policy describes how the organization collects, uses, discloses, and protects personal information in compliance with applicable privacy regulations.", "frequency": "yearly", + "department": "gov", "usedBy": { "soc2": ["P1.1", "P1.2", "P1.3"] } @@ -86,7 +87,7 @@ "content": [ { "type": "text", - "text": "This policy outlines the organization’s practices for handling personal data, including collection, processing, retention, and disposal, to ensure compliance with privacy regulations." + "text": "This policy outlines the organization's practices for handling personal data, including collection, processing, retention, and disposal, to ensure compliance with privacy regulations." } ] }, diff --git a/packages/data/policies/risk_assessment.json b/packages/data/policies/risk_assessment.json index 3d34b31bea..9762825e2c 100644 --- a/packages/data/policies/risk_assessment.json +++ b/packages/data/policies/risk_assessment.json @@ -6,6 +6,7 @@ "name": "Risk Assessment Policy", "description": "This policy outlines the requirements for conducting risk assessments to identify, evaluate, and mitigate risks associated with the organization's information systems, operations, and assets.", "frequency": "yearly", + "department": "gov", "usedBy": { "soc2": ["CC3.2", "CC3.4", "CC8.1"] } diff --git a/packages/data/policies/risk_management.json b/packages/data/policies/risk_management.json index 60c62f929d..1df96feeae 100644 --- a/packages/data/policies/risk_management.json +++ b/packages/data/policies/risk_management.json @@ -4,8 +4,9 @@ "id": "risk_management", "slug": "risk-management-policy", "name": "Risk Management Policy", - "description": "This policy defines the process for identifying, assessing, and mitigating risks to the organization’s objectives and information assets.", + "description": "This policy defines the process for identifying, assessing, and mitigating risks to the organization's objectives and information assets.", "frequency": "yearly", + "department": "gov", "usedBy": { "soc2": ["CC3.1", "CC3.2", "CC3.3", "CC4.2"] } @@ -86,7 +87,7 @@ "content": [ { "type": "text", - "text": "This policy establishes the framework and process for identifying, assessing, and mitigating risks that could impact the organization’s objectives. It applies to all business units and processes." + "text": "This policy establishes the framework and process for identifying, assessing, and mitigating risks that could impact the organization's objectives. It applies to all business units and processes." } ] }, diff --git a/packages/data/policies/software_development.json b/packages/data/policies/software_development.json index 4797940700..b24c18f079 100644 --- a/packages/data/policies/software_development.json +++ b/packages/data/policies/software_development.json @@ -6,6 +6,7 @@ "name": "Software Development Lifecycle Policy", "description": "This policy outlines the requirements for the software development lifecycle to ensure secure, reliable, and high-quality software development practices.", "frequency": "yearly", + "department": "it", "usedBy": { "soc2": ["CC6.2", "CC7.1", "CC7.2", "CC8.1"] } diff --git a/packages/data/policies/system_change.json b/packages/data/policies/system_change.json index de9dbf4064..5b9a1a26ca 100644 --- a/packages/data/policies/system_change.json +++ b/packages/data/policies/system_change.json @@ -6,6 +6,7 @@ "name": "System Change Policy", "description": "This policy outlines the requirements for system changes.", "frequency": "yearly", + "department": "it", "usedBy": { "soc2": ["CC3.4", "CC6.8", "CC7.1", "A1.1"] } diff --git a/packages/data/policies/thirdparty.json b/packages/data/policies/thirdparty.json index 9a258016f6..122a73aa3a 100644 --- a/packages/data/policies/thirdparty.json +++ b/packages/data/policies/thirdparty.json @@ -4,8 +4,9 @@ "id": "thirdparty", "slug": "thirdparty", "name": "Third-Party Management Policy", - "description": "This policy defines the rules for relationships with the organization’s Information Technology (IT) third-parties and partners.", + "description": "This policy defines the rules for relationships with the organization's Information Technology (IT) third-parties and partners.", "frequency": "yearly", + "department": "gov", "usedBy": { "soc2": ["CC2.3", "CC7.3", "CC8.1"] } @@ -95,7 +96,7 @@ "content": [ { "type": "text", - "text": "This policy defines the rules for relationships with the organization’s Information Technology (IT) third-parties and partners." + "text": "This policy defines the rules for relationships with the organization's Information Technology (IT) third-parties and partners." } ] } @@ -109,7 +110,7 @@ "content": [ { "type": "text", - "text": "This policy applies to all IT third-parties and partners who can impact the confidentiality, integrity, and availability of the organization’s technology and sensitive information, or who are within the scope of the organization’s information security program." + "text": "This policy applies to all IT third-parties and partners who can impact the confidentiality, integrity, and availability of the organization's technology and sensitive information, or who are within the scope of the organization's information security program." } ] } @@ -195,7 +196,7 @@ "content": [ { "type": "text", - "text": "IT third-parties are prohibited from accessing the organization’s information security assets until a contract containing security controls is agreed to and signed by the appropriate parties." + "text": "IT third-parties are prohibited from accessing the organization's information security assets until a contract containing security controls is agreed to and signed by the appropriate parties." } ] } @@ -251,7 +252,7 @@ "content": [ { "type": "text", - "text": "Before entering into a contract and gaining access to the organization’s information systems, IT third-parties must undergo a risk assessment." + "text": "Before entering into a contract and gaining access to the organization's information systems, IT third-parties must undergo a risk assessment." } ] } diff --git a/packages/data/policies/vendor_risk_management.json b/packages/data/policies/vendor_risk_management.json index 48d7ba7c4c..5c212853a6 100644 --- a/packages/data/policies/vendor_risk_management.json +++ b/packages/data/policies/vendor_risk_management.json @@ -6,6 +6,7 @@ "name": "Vendor Risk Management Policy", "description": "This policy outlines the criteria and procedures for evaluating, selecting, and monitoring third-party vendors to manage risks associated with external service providers.", "frequency": "yearly", + "department": "gov", "usedBy": { "soc2": ["CC9.2"] } diff --git a/packages/data/policies/workstation.json b/packages/data/policies/workstation.json index 858d7d23d9..d928d7e36b 100644 --- a/packages/data/policies/workstation.json +++ b/packages/data/policies/workstation.json @@ -6,6 +6,7 @@ "name": "Workstation Policy", "description": "This policy outlines the requirements for workstations to ensure secure, reliable, and high-quality software development practices.", "frequency": "yearly", + "department": "it", "usedBy": { "soc2": ["CC6.2", "CC6.7", "CC7.2"] } diff --git a/packages/db/prisma/migrations/20250321211335_add_frequency_to_policy_table/migration.sql b/packages/db/prisma/migrations/20250321211335_add_frequency_to_policy_table/migration.sql new file mode 100644 index 0000000000..dc59f48c31 --- /dev/null +++ b/packages/db/prisma/migrations/20250321211335_add_frequency_to_policy_table/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Policy" ADD COLUMN "department" "Departments" DEFAULT 'none'; diff --git a/packages/db/prisma/schema/policy.prisma b/packages/db/prisma/schema/policy.prisma index f59e4f72a1..e3ba87d204 100644 --- a/packages/db/prisma/schema/policy.prisma +++ b/packages/db/prisma/schema/policy.prisma @@ -33,6 +33,7 @@ model Policy { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt frequency Frequency? + department Departments? @default(none) isRequiredToSign Boolean @default(false) ControlRequirement ControlRequirement[] employeeAcceptances EmployeePolicyAcceptance[] diff --git a/packages/db/prisma/seed.ts b/packages/db/prisma/seed.ts index 82f1e66130..a96e2f5657 100644 --- a/packages/db/prisma/seed.ts +++ b/packages/db/prisma/seed.ts @@ -1,5 +1,5 @@ import { Departments, PrismaClient } from "@prisma/client"; -import type { Frequency, Prisma } from "@prisma/client"; +import type { Evidence, Frequency, Prisma } from "@prisma/client"; import { RequirementType } from "@prisma/client"; import { readFileSync, readdirSync } from "node:fs"; import { join } from "node:path"; @@ -42,6 +42,10 @@ async function main() { await seedPolicies(); console.log("✅ Policies seeded"); + console.log("\n🔗 Seeding evidence records (phase 1)"); + await seedEvidenceRecords(); + console.log("✅ Evidence records seeded"); + console.log("\n🏗️ Seeding frameworks..."); await seedFrameworks(); console.log("✅ Frameworks seeded"); @@ -50,9 +54,13 @@ async function main() { await seedPolicyFramework(); console.log("✅ Policy frameworks seeded"); - console.log("\n🔗 Seeding evidence"); - await seedEvidence(); - console.log("✅ Evidence seeded"); + console.log("\n🔄 Updating policy links (phase 1)"); + await updatePolicyLinks(); + console.log("✅ Policy links updated"); + + console.log("\n🔄 Updating evidence links (phase 2)"); + await updateEvidenceLinks(); + console.log("✅ Evidence links updated"); console.log("\n🎉 All data seeded successfully!"); } @@ -111,6 +119,7 @@ async function seedPolicies() { content: policyData.content as Prisma.InputJsonValue[], usedBy: policyData.metadata.usedBy as Prisma.InputJsonValue, frequency: policyData.metadata?.frequency ?? null, + department: policyData.metadata?.department ?? Departments.none, }, create: { id: policyData.metadata.id, @@ -120,6 +129,7 @@ async function seedPolicies() { content: policyData.content as Prisma.InputJsonValue[], usedBy: policyData.metadata.usedBy as Prisma.InputJsonValue, frequency: policyData.metadata?.frequency ?? null, + department: policyData.metadata?.department ?? Departments.none, }, }); console.log(` ✅ ${file} processed`); @@ -242,7 +252,7 @@ async function seedFrameworkCategoryControls( filteredControlsData )) { // First, upsert the controls itself for the given category. - const insertedControl = await prisma.control.upsert({ + await prisma.control.upsert({ where: { code: controlCode }, update: { name: controlData.name, @@ -266,20 +276,8 @@ async function seedFrameworkCategoryControls( ` 📝 Processing ${controlData.requirements.length} requirements for ${controlCode}` ); for (const requirement of controlData.requirements) { - // For policy requirements, verify the policy exists first - if (requirement.type === "policy" && requirement.policyId) { - const policy = await prisma.policy.findUnique({ - where: { id: requirement.policyId }, - }); - - if (!policy) { - console.log( - ` ⚠️ Policy ${requirement.policyId} not found for requirement ${requirement.id}, skipping` - ); - continue; - } - } - + // For both policy and evidence requirements, initially set policyId and evidenceId to null + // They will be updated later in their respective update functions await prisma.controlRequirement.upsert({ where: { id: requirement.id, @@ -287,23 +285,19 @@ async function seedFrameworkCategoryControls( create: { id: requirement.id, controlId: controlCode, - name: requirement.name, + name: requirement.name || "", type: requirement.type as RequirementType, - description: requirement.description, - policyId: - (requirement.type as RequirementType) === "policy" - ? requirement.policyId - : null, + description: requirement.description || "", + // Set both policyId and evidenceId to null initially + policyId: null, + evidenceId: null, frequency: requirement?.frequency ?? null, department: requirement?.department ?? Departments.none, }, update: { - name: requirement.name, - description: requirement.description, - policyId: - (requirement.type as RequirementType) === "policy" - ? requirement.policyId - : null, + name: requirement.name || "", + description: requirement.description || "", + // Don't update policyId or evidenceId here frequency: requirement?.frequency ?? null, department: requirement?.department ?? Departments.none, }, @@ -378,48 +372,266 @@ async function seedPolicyFramework() { } } -async function seedEvidence() { +// Phase 1: Create evidence records from files (without linking to requirements) +async function seedEvidenceRecords() { + const evidenceDir = join(__dirname, "../../data/evidence"); + const evidenceFiles = readdirSync(evidenceDir).filter((file) => + file.endsWith(".json") + ); + + console.log(`📄 Found ${evidenceFiles.length} evidence files to process`); + + for (const file of evidenceFiles) { + const evidenceId = file.replace(".json", ""); + console.log(` ⏳ Processing evidence file: ${file}...`); + + try { + const fileContent = readFileSync(join(evidenceDir, file), "utf8"); + const evidenceData = JSON.parse(fileContent) as Evidence; + + // Upsert the evidence record + await prisma.evidence.upsert({ + where: { + id: evidenceData.id, + }, + update: { + name: evidenceData.name, + description: evidenceData.description, + frequency: evidenceData.frequency ?? null, + department: evidenceData.department ?? Departments.none, + }, + create: { + id: evidenceData.id, + name: evidenceData.name, + description: evidenceData.description, + frequency: evidenceData.frequency ?? null, + department: evidenceData.department ?? Departments.none, + }, + }); + console.log(` ✅ Evidence ${evidenceId} processed`); + } catch (error) { + console.error(` ❌ Error processing ${file}:`, error); + if (error instanceof Error) { + console.error(` Error details: ${error.message}`); + } + } + } +} + +// Phase 2: Update control requirements to link to evidence +async function updateEvidenceLinks() { + // Get all control requirements that are evidence type const evidenceRequirements = await prisma.controlRequirement.findMany({ where: { type: RequirementType.evidence, }, }); - console.log(`🔄 Processing ${evidenceRequirements.length} evidences`); + console.log( + `🔄 Processing ${evidenceRequirements.length} evidence requirements` + ); + + for (const requirement of evidenceRequirements) { + // Get the controls file for this requirement to extract the evidenceId + const control = await prisma.control.findUnique({ + where: { id: requirement.controlId }, + include: { frameworkCategory: true }, + }); + + if (!control) { + console.log( + ` ⚠️ Control not found for requirement ${requirement.id}, skipping` + ); + continue; + } + + if (!control.frameworkCategory) { + console.log( + ` ⚠️ Framework category not found for control ${control.id}, skipping` + ); + continue; + } + + // Get the framework ID from the category + const frameworkId = control.frameworkCategory.frameworkId; - for (const evidenceReq of evidenceRequirements) { - console.log(` ⏳ Processing evidence: ${evidenceReq.name}...`); + // Get the controls data from the file + const controlsFile = join( + __dirname, + `../../data/controls/${frameworkId}.json` + ); + const controlsData = JSON.parse( + fs.readFileSync(controlsFile, "utf8") + ) as Record; + + // Find the requirement in the control data + const controlData = controlsData[control.code]; + if (!controlData) { + console.log( + ` ⚠️ Control data not found for ${control.code} in framework ${frameworkId}, skipping` + ); + continue; + } + + const reqData = controlData.requirements.find( + (req) => req.id === requirement.id + ); + if (!reqData) { + console.log( + ` ⚠️ Requirement data not found for ${requirement.id} in control ${control.code}, skipping` + ); + continue; + } + + // Get the evidenceId from the requirement data + const evidenceId = reqData.evidenceId; + if (!evidenceId) { + console.log( + ` ⚠️ No evidenceId found for requirement ${requirement.id}, skipping` + ); + continue; + } + + // Verify the evidence exists + const evidence = await prisma.evidence.findUnique({ + where: { id: evidenceId }, + }); - // Create the evidence record with the same ID as the requirement - const evidence = await prisma.evidence.upsert({ + if (!evidence) { + console.log( + ` ⚠️ Evidence ${evidenceId} not found for requirement ${requirement.id}, skipping` + ); + continue; + } + + console.log( + ` ⏳ Linking requirement ${requirement.id} to evidence ${evidenceId}...` + ); + + // Update the control requirement to link to the evidence + await prisma.controlRequirement.update({ where: { - id: evidenceReq.id, + id: requirement.id, }, - update: { - name: evidenceReq.name, - description: evidenceReq.description, - frequency: evidenceReq.frequency ?? null, - department: evidenceReq.department ?? Departments.none, - }, - create: { - id: evidenceReq.id, - name: evidenceReq.name, - description: evidenceReq.description, - frequency: evidenceReq.frequency ?? null, - department: evidenceReq.department ?? Departments.none, + data: { + evidenceId: evidenceId, + name: evidence.name, + description: evidence.description, + frequency: evidence.frequency, + department: evidence.department, }, }); - // Update the control requirement to link back to the evidence + console.log( + ` ✅ Requirement ${requirement.id} linked to evidence ${evidenceId}` + ); + } +} + +// Phase 1: Update control requirements to link to policies +async function updatePolicyLinks() { + // Get all control requirements that are policy type + const policyRequirements = await prisma.controlRequirement.findMany({ + where: { + type: RequirementType.policy, + }, + }); + + console.log(`🔄 Processing ${policyRequirements.length} policy requirements`); + + for (const requirement of policyRequirements) { + // Get the controls file for this requirement to extract the policyId + const control = await prisma.control.findUnique({ + where: { id: requirement.controlId }, + include: { frameworkCategory: true }, + }); + + if (!control) { + console.log( + ` ⚠️ Control not found for requirement ${requirement.id}, skipping` + ); + continue; + } + + if (!control.frameworkCategory) { + console.log( + ` ⚠️ Framework category not found for control ${control.id}, skipping` + ); + continue; + } + + // Get the framework ID from the category + const frameworkId = control.frameworkCategory.frameworkId; + + // Get the controls data from the file + const controlsFile = join( + __dirname, + `../../data/controls/${frameworkId}.json` + ); + const controlsData = JSON.parse( + fs.readFileSync(controlsFile, "utf8") + ) as Record; + + // Find the requirement in the control data + const controlData = controlsData[control.code]; + if (!controlData) { + console.log( + ` ⚠️ Control data not found for ${control.code} in framework ${frameworkId}, skipping` + ); + continue; + } + + const reqData = controlData.requirements.find( + (req) => req.id === requirement.id + ); + if (!reqData) { + console.log( + ` ⚠️ Requirement data not found for ${requirement.id} in control ${control.code}, skipping` + ); + continue; + } + + // Get the policyId from the requirement data + const policyId = reqData.policyId; + if (!policyId) { + console.log( + ` ⚠️ No policyId found for requirement ${requirement.id}, skipping` + ); + continue; + } + + // Verify the policy exists + const policy = await prisma.policy.findUnique({ + where: { id: policyId }, + }); + + if (!policy) { + console.log( + ` ⚠️ Policy ${policyId} not found for requirement ${requirement.id}, skipping` + ); + continue; + } + + console.log( + ` ⏳ Linking requirement ${requirement.id} to policy ${policyId}...` + ); + + // Update the control requirement to link to the policy await prisma.controlRequirement.update({ where: { - id: evidenceReq.id, + id: requirement.id, }, data: { - evidenceId: evidence.id, + policyId: policyId, + name: policy.name, + description: policy.description || "", + frequency: policy.frequency, + department: policy.department ?? Departments.none, }, }); - console.log(` ✅ Evidence ${evidenceReq.name} processed and linked`); + console.log( + ` ✅ Requirement ${requirement.id} linked to policy ${policyId}` + ); } } diff --git a/packages/db/prisma/seedTypes.ts b/packages/db/prisma/seedTypes.ts index 1de159c4e5..11c8e29443 100644 --- a/packages/db/prisma/seedTypes.ts +++ b/packages/db/prisma/seedTypes.ts @@ -17,6 +17,7 @@ export interface Requirement { type: string; description: string; policyId?: string; + evidenceId?: string; name?: string; frequency?: Frequency; department?: Departments; @@ -36,6 +37,7 @@ export interface ControlRequirement { type: string; description: string; policyId?: string; + evidenceId?: string; } export interface Policy { @@ -49,6 +51,7 @@ export interface Policy { [key: string]: string[]; }; frequency?: Frequency; + department?: Departments; }; content: Array<{ type: string;