diff --git a/apps/app/src/actions/framework/select-frameworks-action.ts b/apps/app/src/actions/framework/select-frameworks-action.ts index d1311c00c4..af1aae8783 100644 --- a/apps/app/src/actions/framework/select-frameworks-action.ts +++ b/apps/app/src/actions/framework/select-frameworks-action.ts @@ -4,6 +4,7 @@ import { db, type Policy, type User } from "@bubba/db"; import { authActionClient } from "../safe-action"; import { z } from "zod"; import type { ActionData } from "../types"; +import type { InputJsonValue } from "@prisma/client/runtime/library"; const selectFrameworksSchema = z.object({ frameworkIds: z.array(z.string()), @@ -136,6 +137,7 @@ const createOrganizationPolicy = async (user: User, frameworkIds: string[]) => { organizationId: user.organizationId!, policyId: policy.id, status: "draft", + content: policy.content as InputJsonValue[], })), }); diff --git a/apps/app/src/actions/policies/update-policy-action.ts b/apps/app/src/actions/policies/update-policy-action.ts index 3501eed53c..300162ce41 100644 --- a/apps/app/src/actions/policies/update-policy-action.ts +++ b/apps/app/src/actions/policies/update-policy-action.ts @@ -17,7 +17,7 @@ interface ContentNode { // Simplified content processor that creates a new plain object function processContent( - content: ContentNode | ContentNode[], + content: ContentNode | ContentNode[] ): ContentNode | ContentNode[] { if (!content) return content; @@ -74,8 +74,8 @@ export const updatePolicyAction = authActionClient } try { - const policy = await db.artifact.findUnique({ - where: { id, type: "policy", organizationId: user.organizationId }, + const policy = await db.organizationPolicy.findUnique({ + where: { id, organizationId: user.organizationId }, }); if (!policy) { @@ -87,12 +87,12 @@ export const updatePolicyAction = authActionClient // Create a new plain object from the content const processedContent = JSON.parse( - JSON.stringify(processContent(content as ContentNode)), + JSON.stringify(processContent(content as ContentNode)) ); - await db.artifact.update({ + await db.organizationPolicy.update({ where: { id }, - data: { content: processedContent }, + data: { content: processedContent.content }, }); revalidatePath(`/policies/${id}`); diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/policies/(overview)/Components/PoliciesOverview.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/policies/(overview)/Components/PoliciesOverview.tsx index e4afd1f260..6b3cbf05ed 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/policies/(overview)/Components/PoliciesOverview.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/policies/(overview)/Components/PoliciesOverview.tsx @@ -1,9 +1,8 @@ "use client"; -import { PoliciesByAssignee } from "@/components/policies/charts/policies-by-assignee"; import { PoliciesByFramework } from "@/components/policies/charts/policies-by-framework"; -import { usePolicies } from "../hooks/usePolicies"; import { Skeleton } from "@bubba/ui/skeleton"; +import { usePolicies } from "../../hooks/usePolicies"; export function PoliciesOverview() { const { data, isLoading, error } = usePolicies(); diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/policies/(overview)/Actions/get-policies.ts b/apps/app/src/app/[locale]/(app)/(dashboard)/policies/Actions/get-policies.ts similarity index 100% rename from apps/app/src/app/[locale]/(app)/(dashboard)/policies/(overview)/Actions/get-policies.ts rename to apps/app/src/app/[locale]/(app)/(dashboard)/policies/Actions/get-policies.ts diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/policies/Actions/get-policy.ts b/apps/app/src/app/[locale]/(app)/(dashboard)/policies/Actions/get-policy.ts new file mode 100644 index 0000000000..4c678e084b --- /dev/null +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/policies/Actions/get-policy.ts @@ -0,0 +1,58 @@ +"use server"; + +import { authActionClient } from "@/actions/safe-action"; +import { db, type OrganizationPolicy } from "@bubba/db"; +import { z } from "zod"; + +const schema = z.object({ + policyId: z.string(), +}); + +export type PolicyStatsResponse = { + success: boolean; + data?: OrganizationPolicy[]; + error?: string; +}; + +export const getPolicy = authActionClient + .schema(schema) + .metadata({ + name: "get-policy", + track: { + event: "get-policy", + channel: "server", + }, + }) + .action(async ({ ctx, parsedInput }) => { + const { user } = ctx; + const { policyId } = parsedInput; + + if (!user.organizationId) { + return { + success: false, + error: "Not authorized - no organization found", + }; + } + + try { + const policy = await db.organizationPolicy.findFirst({ + where: { + organizationId: user.organizationId!, + id: policyId, + }, + include: { + policy: true, + }, + }); + + return { + success: true, + data: policy, + }; + } catch (error) { + return { + success: false, + error: "Failed to fetch policy statistics", + }; + } + }); diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/policies/[id]/layout.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/policies/[id]/layout.tsx index 0e53af7c23..13e511a661 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/policies/[id]/layout.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/policies/[id]/layout.tsx @@ -1,8 +1,4 @@ import { auth } from "@/auth"; -import { Title } from "@/components/title"; -import { getI18n } from "@/locales/server"; -import { db } from "@bubba/db"; -import { unstable_cache } from "next/cache"; import { redirect } from "next/navigation"; export default async function Layout({ @@ -12,7 +8,6 @@ export default async function Layout({ children: React.ReactNode; params: Promise<{ id: string }>; }) { - const t = await getI18n(); const { id } = await params; const session = await auth(); @@ -24,40 +19,9 @@ export default async function Layout({ redirect("/policies"); } - const policy = await getPolicy(id, session.user.organizationId); - - if (!policy) { - redirect("/policies"); - } - return (
- - <main className="h-[calc(100vh-4rem-4rem)]">{children}</main> </div> ); - - /* return ( - <div className="h-[calc(100vh-4rem)] bg-background max-w-[1200px]"> - <div className="h-16 px-4 border-b flex items-center"> - <Title title={policy.name} href="/policies/all" /> - </div> - <main className="h-[calc(100vh-4rem-4rem)]">{children}</main> - </div> - ); */ } - -const getPolicy = unstable_cache( - async (policyId: string, organizationId: string) => { - const policy = await db.artifact.findUnique({ - where: { - id: policyId, - organizationId: organizationId, - }, - }); - - return policy; - }, - ["policy-cache"], -); diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/policies/[id]/page.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/policies/[id]/page.tsx index 30b12ee840..da98f65d53 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/policies/[id]/page.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/policies/[id]/page.tsx @@ -1,7 +1,5 @@ import { auth } from "@/auth"; import { PolicyOverview } from "@/components/policies/policy-overview"; -import { db } from "@bubba/db"; -import { unstable_cache } from "next/cache"; import { redirect } from "next/navigation"; interface PageProps { @@ -9,50 +7,7 @@ interface PageProps { } export default async function PolicyPage({ params }: PageProps) { - const session = await auth(); const { id } = await params; - if (!session) { - redirect("/login"); - } - - if (!session.user.organizationId || !id) { - redirect("/"); - } - - const policy = await getPolicy(id, session.user.organizationId); - - if (!policy) { - redirect("/policies"); - } - - const users = await getUsers(session.user.organizationId); - - return <PolicyOverview policy={policy} />; + return <PolicyOverview policyId={id} />; } - -const getPolicy = unstable_cache( - async (id: string, organizationId: string) => { - const policy = await db.artifact.findUnique({ - where: { - id, - type: "policy", - organizationId: organizationId, - }, - }); - - return policy; - }, - ["policy-cache"], -); - -const getUsers = unstable_cache( - async (organizationId: string) => { - const users = await db.user.findMany({ - where: { organizationId: organizationId }, - }); - - return users; - }, - ["users-cache"], -); diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/policies/(overview)/hooks/usePolicies.ts b/apps/app/src/app/[locale]/(app)/(dashboard)/policies/hooks/usePolicies.ts similarity index 100% rename from apps/app/src/app/[locale]/(app)/(dashboard)/policies/(overview)/hooks/usePolicies.ts rename to apps/app/src/app/[locale]/(app)/(dashboard)/policies/hooks/usePolicies.ts diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/policies/hooks/usePolicy.ts b/apps/app/src/app/[locale]/(app)/(dashboard)/policies/hooks/usePolicy.ts new file mode 100644 index 0000000000..67b16b622a --- /dev/null +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/policies/hooks/usePolicy.ts @@ -0,0 +1,42 @@ +"use client"; + +import { getPolicy } from "../Actions/get-policy"; +import useSWR from "swr"; +import type { OrganizationPolicy, Policy } from "@bubba/db"; + +const POLICY_KEY = "policy"; + +type OrganizationPolicyWithPolicy = OrganizationPolicy & { + policy: Policy; +}; + +async function fetchPolicy( + policyId: string +): Promise<OrganizationPolicyWithPolicy> { + const response = await getPolicy({ policyId }); + + if (!response?.data?.success || !response.data.data) { + throw new Error(response?.data?.error || "Failed to fetch policy data"); + } + + return response.data.data; +} + +export function usePolicy({ policyId }: { policyId: string }) { + const { data, error, isLoading, mutate } = + useSWR<OrganizationPolicyWithPolicy>( + [POLICY_KEY, policyId], + () => fetchPolicy(policyId), + { + revalidateOnFocus: false, + revalidateOnReconnect: false, + } + ); + + return { + data, + isLoading, + error, + mutate, + }; +} diff --git a/apps/app/src/components/editor/advanced-editor.tsx b/apps/app/src/components/editor/advanced-editor.tsx index b7f2158e0a..5fea2f381c 100644 --- a/apps/app/src/components/editor/advanced-editor.tsx +++ b/apps/app/src/components/editor/advanced-editor.tsx @@ -35,8 +35,14 @@ const extensions = [...defaultExtensions, slashCommand]; const PolicyEditor = ({ policyId, content, -}: { policyId: string; content: JSONContent }) => { - const [initialContent, setInitialContent] = useState<JSONContent>(content); +}: { + policyId: string; + content: JSONContent; +}) => { + const [initialContent, setInitialContent] = useState<JSONContent>({ + type: "doc", + content: content as JSONContent[], + }); const [saveStatus, setSaveStatus] = useState("Saved"); const [charsCount, setCharsCount] = useState(); @@ -74,7 +80,7 @@ const PolicyEditor = ({ content: json, }); }, - 1000, + 1000 ); if (!initialContent) return null; diff --git a/apps/app/src/components/policies/charts/policies-by-framework.tsx b/apps/app/src/components/policies/charts/policies-by-framework.tsx index 1b533fb1be..2c13fa988b 100644 --- a/apps/app/src/components/policies/charts/policies-by-framework.tsx +++ b/apps/app/src/components/policies/charts/policies-by-framework.tsx @@ -1,6 +1,6 @@ "use client"; -import { usePolicies } from "@/app/[locale]/(app)/(dashboard)/policies/(overview)/hooks/usePolicies"; +import { usePolicies } from "@/app/[locale]/(app)/(dashboard)/policies/hooks/usePolicies"; import { useI18n } from "@/locales/client"; import { Card, CardContent, CardHeader, CardTitle } from "@bubba/ui/card"; import { diff --git a/apps/app/src/components/policies/policy-overview.tsx b/apps/app/src/components/policies/policy-overview.tsx index ab6847595f..286b9b5293 100644 --- a/apps/app/src/components/policies/policy-overview.tsx +++ b/apps/app/src/components/policies/policy-overview.tsx @@ -1,17 +1,21 @@ "use client"; -import type { Artifact } from "@bubba/db"; import type { JSONContent } from "@tiptap/react"; import PolicyEditor from "../editor/advanced-editor"; +import { usePolicy } from "@/app/[locale]/(app)/(dashboard)/policies/hooks/usePolicy"; + +export function PolicyOverview({ policyId }: { policyId: string }) { + const { data: policy } = usePolicy({ policyId }); + + if (!policy) return null; -export function PolicyOverview({ policy }: { policy: Artifact }) { const content = policy.content as JSONContent; if (!content) return null; return ( <div className="h-[calc(100vh-8rem)] flex flex-col"> - <PolicyEditor policyId={policy.id} content={content} /> + <PolicyEditor policyId={policyId} content={content} /> </div> ); } diff --git a/apps/app/src/jobs/seed/policies/access.json b/apps/app/src/jobs/seed/policies/access.json index f659860528..393c8ecf89 100644 --- a/apps/app/src/jobs/seed/policies/access.json +++ b/apps/app/src/jobs/seed/policies/access.json @@ -1,7 +1,15 @@ { "type": "doc", "metadata": { - "controls": ["CC6.1", "CC6.2", "CC6.3"] + "id": "access-onboarding-and-termination-policy", + "slug": "access-onboarding-and-termination-policy", + "name": "Access Onboarding and Termination Policy", + "description": "This policy outlines the procedures for onboarding and offboarding users to technical infrastructure.", + "usedBy": { + "soc2": { + "CC6": ["CC6.1", "CC6.2", "CC6.3"] + } + } }, "content": [ { diff --git a/apps/app/src/jobs/seed/policies/application.json b/apps/app/src/jobs/seed/policies/application.json index 9a16abf0e9..5d6e7d1bda 100644 --- a/apps/app/src/jobs/seed/policies/application.json +++ b/apps/app/src/jobs/seed/policies/application.json @@ -1,7 +1,7 @@ { "type": "doc", "metadata": { - "controls": ["CC6.2"] + "controls": ["CC7.1", "CC7.2", "CC7.4"] }, "content": [ { diff --git a/packages/data/categories/soc2.json b/packages/data/categories/soc2.json index 8197c50cb4..3ca716c97b 100644 --- a/packages/data/categories/soc2.json +++ b/packages/data/categories/soc2.json @@ -43,5 +43,25 @@ "name": "CC9: Risk Mitigation", "code": "CC9", "description": "This criterion ensures that the organization has a process for mitigating risks to its security posture." + }, + "A1": { + "name": "A1: Availability", + "code": "A1", + "description": "This criterion ensures that systems are available for operation and use as committed or agreed." + }, + "C1": { + "name": "C1: Data Integrity", + "code": "C1", + "description": "This criterion ensures that data is accurate, complete, and consistent." + }, + "PI1": { + "name": "PI1: Processing Integrity", + "code": "PI1", + "description": "This criterion ensures that data is accurate, complete, and consistent." + }, + "P1": { + "name": "P1: Privacy", + "code": "P1", + "description": "This criterion ensures that data is accurate, complete, and consistent." } } diff --git a/packages/data/controls/soc2.json b/packages/data/controls/soc2.json index 94a03d910b..852128952c 100644 --- a/packages/data/controls/soc2.json +++ b/packages/data/controls/soc2.json @@ -704,6 +704,31 @@ } ] }, + "CC7.5": { + "code": "CC7.5", + "name": "Security Event Communication", + "description": "The organization identifies, develops, and implements activities to communicate security incidents to affected parties.", + "domain": "System Operations", + "categoryId": "CC7", + "requirements": [ + { + "id": "CC7.5-policy", + "type": "policy", + "description": "Security event communication policy", + "policyId": "password_policy" + }, + { + "id": "CC7.5-procedure", + "type": "procedure", + "description": "Security event communication procedures" + }, + { + "id": "CC7.5-evidence", + "type": "evidence", + "description": "Security event communication records" + } + ] + }, "CC8.1": { "code": "CC8.1", "name": "Change Authorization", @@ -803,5 +828,209 @@ "description": "BC/DR test results" } ] + }, + "A1.1": { + "code": "A1.1", + "name": "Availability Commitments", + "description": "The entity maintains commitments to ensure systems are available for operation.", + "domain": "Availability", + "categoryId": "A1", + "requirements": [] + }, + "A1.2": { + "code": "A1.2", + "name": "Capacity Planning", + "description": "The entity monitors and manages system capacity to meet demands.", + "domain": "Availability", + "categoryId": "A1", + "requirements": [] + }, + "A1.3": { + "code": "A1.3", + "name": "Incident Recovery", + "description": "The entity has controls to restore system availability after incidents.", + "domain": "Availability", + "categoryId": "A1", + "requirements": [] + }, + "C1.1": { + "code": "C1.1", + "name": "Confidential Information Classification", + "description": "The entity classifies information to identify and protect confidential information.", + "domain": "Confidentiality", + "categoryId": "C1", + "requirements": [ + { + "id": "C1.1-policy", + "type": "policy", + "description": "Information classification policy", + "policyId": "password_policy" + }, + { + "id": "C1.1-procedure", + "type": "procedure", + "description": "Information classification procedures" + } + ] + }, + "C1.2": { + "code": "C1.2", + "name": "Access Restrictions for Confidential Data", + "description": "The entity restricts access to confidential information on a need-to-know basis.", + "domain": "Confidentiality", + "categoryId": "C1", + "requirements": [ + { + "id": "C1.2-policy", + "type": "policy", + "description": "Confidential data access policy", + "policyId": "password_policy" + }, + { + "id": "C1.2-procedure", + "type": "procedure", + "description": "Confidential data access procedures" + } + ] + }, + "C1.3": { + "code": "C1.3", + "name": "Confidential Data Disposal", + "description": "The entity securely disposes of confidential information when no longer needed.", + "domain": "Confidentiality", + "categoryId": "C1", + "requirements": [ + { + "id": "C1.3-policy", + "type": "policy", + "description": "Data disposal policy", + "policyId": "password_policy" + }, + { + "id": "C1.3-procedure", + "type": "procedure", + "description": "Data disposal procedures" + } + ] + }, + "PI1.1": { + "code": "PI1.1", + "name": "Accuracy and Completeness", + "description": "The entity ensures data is processed accurately and completely.", + "domain": "Processing Integrity", + "categoryId": "PI1", + "requirements": [ + { + "id": "PI1.1-policy", + "type": "policy", + "description": "Data accuracy policy", + "policyId": "password_policy" + }, + { + "id": "PI1.1-procedure", + "type": "procedure", + "description": "Data accuracy procedures" + } + ] + }, + "PI1.2": { + "code": "PI1.2", + "name": "Input, Processing, and Output Controls", + "description": "The entity validates the completeness and accuracy of data throughout processing.", + "domain": "Processing Integrity", + "categoryId": "PI1", + "requirements": [ + { + "id": "PI1.2-policy", + "type": "policy", + "description": "Data processing controls policy", + "policyId": "password_policy" + }, + { + "id": "PI1.2-procedure", + "type": "procedure", + "description": "Data processing control procedures" + } + ] + }, + "PI1.3": { + "code": "PI1.3", + "name": "Exception Handling", + "description": "The entity identifies and resolves processing exceptions in a timely manner.", + "domain": "Processing Integrity", + "categoryId": "PI1", + "requirements": [ + { + "id": "PI1.3-policy", + "type": "policy", + "description": "Exception handling policy", + "policyId": "password_policy" + }, + { + "id": "PI1.3-procedure", + "type": "procedure", + "description": "Exception handling procedures" + } + ] + }, + "P1.1": { + "code": "P1.1", + "name": "Privacy Notice", + "description": "The entity provides notice about the collection, use, and disclosure of personal information.", + "domain": "Privacy", + "categoryId": "P1", + "requirements": [ + { + "id": "P1.1-policy", + "type": "policy", + "description": "Privacy notice policy", + "policyId": "password_policy" + }, + { + "id": "P1.1-procedure", + "type": "procedure", + "description": "Privacy notice procedures" + } + ] + }, + "P1.2": { + "code": "P1.2", + "name": "Choice and Consent", + "description": "The entity obtains consent for personal information where required by policy or law.", + "domain": "Privacy", + "categoryId": "P1", + "requirements": [ + { + "id": "P1.2-policy", + "type": "policy", + "description": "Consent policy", + "policyId": "password_policy" + }, + { + "id": "P1.2-procedure", + "type": "procedure", + "description": "Consent procedures" + } + ] + }, + "P1.3": { + "code": "P1.3", + "name": "Data Retention and Disposal", + "description": "The entity retains personal information for only as long as needed and disposes of it securely.", + "domain": "Privacy", + "categoryId": "P1", + "requirements": [ + { + "id": "P1.3-policy", + "type": "policy", + "description": "Data retention policy", + "policyId": "password_policy" + }, + { + "id": "P1.3-procedure", + "type": "procedure", + "description": "Data retention procedures" + } + ] } } diff --git a/packages/data/policies/access.json b/packages/data/policies/access.json new file mode 100644 index 0000000000..4c7c1c129e --- /dev/null +++ b/packages/data/policies/access.json @@ -0,0 +1,507 @@ +{ + "type": "doc", + "metadata": { + "id": "access-onboarding-and-termination-policy", + "slug": "access-onboarding-and-termination-policy", + "name": "Access Onboarding and Termination Policy", + "description": "This policy outlines the procedures for onboarding and offboarding users to technical infrastructure.", + "usedBy": { + "soc2": ["CC6.1", "CC6.2", "CC6.4", "CC6.8"] + } + }, + "content": [ + { + "type": "heading", + "attrs": { + "level": 1 + }, + "content": [ + { + "type": "text", + "text": "Access Onboarding and Termination Policy" + } + ] + }, + { + "type": "heading", + "attrs": { + "level": 2 + }, + "content": [ + { + "type": "text", + "text": "Policy Information" + } + ] + }, + { + "type": "table", + "content": [ + { + "type": "tableRow", + "content": [ + { + "type": "tableCell", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Organization" + } + ] + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Last Review" + } + ] + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Review Frequency" + } + ] + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Approved By" + } + ] + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Classification" + } + ] + } + ] + } + ] + }, + { + "type": "tableRow", + "content": [ + { + "type": "tableCell", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "{{organization}}" + } + ] + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "{{date}}" + } + ] + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Annual" + } + ] + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Chief Information Security Officer" + } + ] + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Confidential" + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { "level": 2 }, + "content": [{ "type": "text", "text": "Revision History" }] + }, + { + "type": "table", + "content": [ + { + "type": "tableRow", + "content": [ + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Version" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Date" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Description" }] + } + ] + }, + { + "type": "tableRow", + "content": [ + { + "type": "tableCell", + "content": [{ "type": "text", "text": "1.0" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "{{date}}" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Initial document" }] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { "level": 2 }, + "content": [{ "type": "text", "text": "Purpose and Scope" }] + }, + { + "type": "orderedList", + "attrs": { "tight": true, "start": 1 }, + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "The purpose of this policy is to define procedures to onboard and offboard users to technical infrastructure in a manner that minimizes the risk of information loss or exposure." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "This policy applies to all technical infrastructure within the organization." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "This policy applies to all full-time and part-time employees and contractors." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { "level": 2 }, + "content": [{ "type": "text", "text": "Background" }] + }, + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "The organization relies on the principle of least privilege to minimize the risk of information loss or exposure (from both inside and outside the organization). Account creation and permission levels are restricted to only the resources absolutely needed to perform each person's job duties. When a user's role within the organization changes, those accounts and permission levels are changed/revoked to fit the new role and disabled when the user leaves the organization altogether." + } + ] + }, + { + "type": "heading", + "attrs": { "level": 2 }, + "content": [{ "type": "text", "text": "Policy" }] + }, + { + "type": "heading", + "attrs": { "level": 3 }, + "content": [{ "type": "text", "text": "During Onboarding" }] + }, + { + "type": "orderedList", + "attrs": { "tight": true, "start": 1 }, + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Hiring Manager informs HR upon hire of a new employee." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "HR emails IT to inform them of a new hire and their role." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "IT creates a checklist of accounts and permission levels needed for that role." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "The owner of each resource reviews and approves account creation and the associated permissions." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "IT works with the owner of each resource to set up the user." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { "level": 3 }, + "content": [{ "type": "text", "text": "During Offboarding" }] + }, + { + "type": "orderedList", + "attrs": { "tight": true, "start": 1 }, + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Hiring Manager notifies HR when an employee has been terminated." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "HR sends a weekly email report to IT summarizing list of users terminated and instructs IT to disable their access." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "IT terminates access within five business days from receipt of notification." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { "level": 3 }, + "content": [{ "type": "text", "text": "Role Changes" }] + }, + { + "type": "orderedList", + "attrs": { "tight": true, "start": 1 }, + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Hiring Manager will inform HR of a change in role." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "HR and IT will follow the same steps as outlined in the onboarding and offboarding procedures." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { "level": 3 }, + "content": [{ "type": "text", "text": "Account Reviews" }] + }, + { + "type": "orderedList", + "attrs": { "tight": true, "start": 1 }, + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Each month, IT and HR will review accounts and permission levels for accuracy." + } + ] + } + ] + } + ] + } + ] +} diff --git a/packages/data/policies/application.json b/packages/data/policies/application.json new file mode 100644 index 0000000000..f22a4e15c0 --- /dev/null +++ b/packages/data/policies/application.json @@ -0,0 +1,648 @@ +{ + "type": "doc", + "metadata": { + "id": "application-security-policy", + "slug": "application-security-policy", + "name": "Application Security Policy", + "description": "This policy outlines the security framework and requirements for applications, notably web applications, within the organization's production environment.", + "usedBy": { + "soc2": ["CC7.1", "CC7.2", "CC7.4"] + } + }, + "content": [ + { + "type": "heading", + "attrs": { "level": 1 }, + "content": [{ "type": "text", "text": "Application Security Policy" }] + }, + { + "type": "heading", + "attrs": { "level": 2 }, + "content": [{ "type": "text", "text": "Policy Information" }] + }, + { + "type": "table", + "content": [ + { + "type": "tableRow", + "content": [ + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Organization" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Last Review" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Review Frequency" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Approved By" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Classification" }] + } + ] + }, + { + "type": "tableRow", + "content": [ + { + "type": "tableCell", + "content": [{ "type": "text", "text": "{{organization}}" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "{{date}}" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Annual" }] + }, + { + "type": "tableCell", + "content": [ + { "type": "text", "text": "Chief Information Security Officer" } + ] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Confidential" }] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { "level": 2 }, + "content": [{ "type": "text", "text": "Purpose and Scope" }] + }, + { + "type": "orderedList", + "attrs": { "tight": true, "start": 1 }, + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "This application security policy defines the security framework and requirements for applications, notably web applications, within the organization's production environment." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "This document also provides implementing controls and instructions for web application security, to include periodic vulnerability scans and other types of evaluations and assessments." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "This policy applies to all applications within the organization's production environment, as well as administrators and users of these applications. This typically includes employees and contractors." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { "level": 2 }, + "content": [{ "type": "text", "text": "Background" }] + }, + { + "type": "orderedList", + "attrs": { "tight": true, "start": 1 }, + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Application vulnerabilities typically account for the largest number of initial attack vectors after malware infections. As a result, it is important that applications are designed with security in mind, and that they are scanned and continuously monitored for malicious activity that could indicate a system compromise. Discovery and subsequent mitigation of application vulnerabilities will limit the organization's attack surface, and ensures a baseline level of security across all systems." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "In addition to scanning guidance, this policy also defines technical requirements and procedures to ensure that applications are properly hardened in accordance with security best practices." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { "level": 2 }, + "content": [{ "type": "text", "text": "References" }] + }, + { + "type": "orderedList", + "attrs": { "tight": true, "start": 1 }, + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { "type": "text", "text": "Data Classification Policy" } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { "type": "text", "text": "OWASP Risk Rating Methodology" } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [{ "type": "text", "text": "OWASP Testing Guide" }] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [{ "type": "text", "text": "OWASP Top Ten Project" }] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { "level": 2 }, + "content": [{ "type": "text", "text": "Policy" }] + }, + { + "type": "heading", + "attrs": { "level": 3 }, + "content": [{ "type": "text", "text": "Security Best Practices" }] + }, + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "The organization must ensure that all applications it develops and/or acquires are securely configured and managed. The following security best practices must be considered and, if feasible, applied as a matter of the application's security design:" + } + ] + }, + { + "type": "orderedList", + "attrs": { "tight": true, "start": 1 }, + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Data handled and managed by the application must be classified in accordance with the Data Classification Policy." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "If the application processes confidential information, a confidential record banner must be prominently displayed which highlights the type of confidential data being accessed (e.g., personally-identifiable information (PII), protected health information (PHI), etc.)" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { "level": 3 }, + "content": [{ "type": "text", "text": "Third-Party Applications" }] + }, + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "When applications are acquired from a third party, such as a vendor:" + } + ] + }, + { + "type": "orderedList", + "attrs": { "tight": true, "start": 1 }, + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Only applications that are supported by an approved vendor shall be procured and used." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Full support contracts must be arranged with the application vendor for full life-cycle support." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { "level": 3 }, + "content": [{ "type": "text", "text": "Web Application Assessment" }] + }, + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Web applications must be assessed according to the following criteria:" + } + ] + }, + { + "type": "orderedList", + "attrs": { "tight": true, "start": 1 }, + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "New or major application releases must have a full assessment prior to approval of the change control documentation and/or release into the production environment." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { "level": 3 }, + "content": [{ "type": "text", "text": "Vulnerability Risk Levels" }] + }, + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Vulnerabilities discovered during application assessments must be mitigated based upon the following risk levels, which are based on the Open Web Application Security Project (OWASP) Risk Rating Methodology:" + } + ] + }, + { + "type": "heading", + "attrs": { "level": 4 }, + "content": [{ "type": "text", "text": "High Risk" }] + }, + { + "type": "bulletList", + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Issues must be fixed immediately" + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Alternate mitigation strategies must be implemented to limit exposure before deployment" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { "level": 3 }, + "content": [{ "type": "text", "text": "Security Assessment Types" }] + }, + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "The following security assessment types may be leveraged to perform an application security assessment:" + } + ] + }, + { + "type": "heading", + "attrs": { "level": 4 }, + "content": [{ "type": "text", "text": "Full Assessment" }] + }, + { + "type": "bulletList", + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Comprised of tests for all known web application vulnerabilities" + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Uses both automated and manual tools based on the OWASP Testing Guide" + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Must leverage manual penetration testing techniques" + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Validates discovered vulnerabilities to determine overall risk" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { "level": 4 }, + "content": [{ "type": "text", "text": "Quick Assessment" }] + }, + { + "type": "bulletList", + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Consists of an automated scan of an application" + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Covers, at minimum, the OWASP Top Ten web application security risks" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { "level": 4 }, + "content": [{ "type": "text", "text": "Targeted Assessment" }] + }, + { + "type": "bulletList", + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Verifies vulnerability remediation changes" + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Validates new application functionality" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { "level": 3 }, + "content": [{ "type": "text", "text": "Additional Security Controls" }] + }, + { + "type": "orderedList", + "attrs": { "tight": true, "start": 1 }, + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "To counter the risk of unauthorized access, the organization maintains a Data Center Security Policy." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Security requirements for the software development life cycle, including system development, acquisition and maintenance are defined in the Software Development Lifecycle Policy." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Security requirements for handling information security incidents are defined in the Security Incident Response Policy." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Disaster recovery and business continuity management policy is defined in the Disaster Recovery Policy." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Requirements for information system availability and redundancy are defined in the System Availability Policy." + } + ] + } + ] + } + ] + } + ] +} diff --git a/packages/data/policies/availability.json b/packages/data/policies/availability.json new file mode 100644 index 0000000000..199079d1b9 --- /dev/null +++ b/packages/data/policies/availability.json @@ -0,0 +1,709 @@ +{ + "type": "doc", + "metadata": { + "id": "availability-policy", + "slug": "availability-policy", + "name": "Availability Policy", + "description": "This policy outlines the requirements for proper controls to protect the availability of the organization's information systems.", + "usedBy": { + "soc2": ["CC9.1", "CC7.3", "CC7.5", "A1.1", "A1.2"] + } + }, + "content": [ + { + "type": "heading", + "attrs": { "level": 1 }, + "content": [{ "type": "text", "text": "Availability Policy" }] + }, + { + "type": "heading", + "attrs": { "level": 2 }, + "content": [{ "type": "text", "text": "Policy Information" }] + }, + { + "type": "table", + "content": [ + { + "type": "tableRow", + "content": [ + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Organization" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Last Review" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Review Frequency" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Approved By" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Classification" }] + } + ] + }, + { + "type": "tableRow", + "content": [ + { + "type": "tableCell", + "content": [{ "type": "text", "text": "{{organization}}" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "{{date}}" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Annual" }] + }, + { + "type": "tableCell", + "content": [ + { "type": "text", "text": "Chief Information Security Officer" } + ] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Confidential" }] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { "level": 2 }, + "content": [{ "type": "text", "text": "Revision History" }] + }, + { + "type": "table", + "content": [ + { + "type": "tableRow", + "content": [ + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Version" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Date" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Description" }] + } + ] + }, + { + "type": "tableRow", + "content": [ + { + "type": "tableCell", + "content": [{ "type": "text", "text": "1.0" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "{{date}}" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Initial document" }] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { "level": 2 }, + "content": [{ "type": "text", "text": "Purpose and Scope" }] + }, + { + "type": "orderedList", + "attrs": { "tight": true, "start": 1 }, + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "The purpose of this policy is to define requirements for proper controls to protect the availability of the organization's information systems." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "This policy applies to all users of information systems within the organization. This typically includes employees and contractors, as well as any external parties that come into contact with systems and information controlled by the organization (hereinafter referred to as \"users\"). This policy must be made readily available to all users." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { "level": 2 }, + "content": [{ "type": "text", "text": "Background" }] + }, + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "The intent of this policy is to minimize the amount of unexpected or unplanned downtime (also known as outages) of information systems under the organization's control. This policy prescribes specific measures for the organization that will increase system redundancy, introduce failover mechanisms, and implement monitoring such that outages are prevented as much as possible. Where they cannot be prevented, outages will be quickly detected and remediated." + } + ] + }, + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Within this policy, availability is defined as a characteristic of information or information systems in which such information or systems can be accessed by authorized entities whenever needed." + } + ] + }, + { + "type": "heading", + "attrs": { "level": 2 }, + "content": [{ "type": "text", "text": "References" }] + }, + { + "type": "orderedList", + "attrs": { "tight": true, "start": 1 }, + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [{ "type": "text", "text": "Risk Assessment Policy" }] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { "level": 2 }, + "content": [{ "type": "text", "text": "Policy" }] + }, + { + "type": "heading", + "attrs": { "level": 3 }, + "content": [ + { "type": "text", "text": "System Availability Requirements" } + ] + }, + { + "type": "orderedList", + "attrs": { "tight": true, "start": 1 }, + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Information systems must be consistently available to conduct and support business operations." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Information systems must have a defined availability classification, with appropriate controls enabled and incorporated into development and production processes based on this classification." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "System and network failures must be reported promptly to the organization's lead for Information Technology (IT) or designated IT operations manager." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Users must be notified of scheduled outages (e.g., system maintenance) that require periods of downtime. This notification must specify the date and time of the system maintenance, expected duration, and anticipated system or service resumption time." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Prior to production use, each new or significantly modified application must have a completed risk assessment that includes availability risks. Risk assessments must be completed in accordance with the Risk Assessment Policy." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Capacity management and load balancing techniques must be used, as deemed necessary, to help minimize the risk and impact of system failures." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { "level": 3 }, + "content": [{ "type": "text", "text": "Backup Requirements" }] + }, + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Information systems must have an appropriate data backup plan that ensures:" + } + ] + }, + { + "type": "orderedList", + "attrs": { "tight": true, "start": 1 }, + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "All sensitive data can be restored within a reasonable time period." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Full backups of critical resources are performed on at least a weekly basis." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Incremental backups for critical resources are performed on at least a daily basis." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Backups and associated media are maintained for a minimum of thirty (30) days and retained for at least one (1) year, or in accordance with legal and regulatory requirements." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Backups are stored off-site with multiple points of redundancy and protected using encryption and key management." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Tests of backup data must be conducted once per quarter. Tests of configurations must be conducted twice per year." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { "level": 3 }, + "content": [{ "type": "text", "text": "Redundancy and Failover" }] + }, + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Information systems must have an appropriate redundancy and failover plan that meets the following criteria:" + } + ] + }, + { + "type": "orderedList", + "attrs": { "tight": true, "start": 1 }, + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Network infrastructure that supports critical resources must have system-level redundancy (including but not limited to a secondary power supply, backup disk-array, and secondary computing system)." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Critical core components must have an actively maintained spare. SLAs must require parts replacement within twenty-four (24) hours." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Servers that support critical resources must have redundant power supplies and network interface cards." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Servers classified as high availability must use disk mirroring." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { "level": 3 }, + "content": [{ "type": "text", "text": "Business Continuity" }] + }, + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Information systems must have an appropriate business continuity plan that adheres to the following availability classifications and requirements:" + } + ] + }, + { + "type": "table", + "content": [ + { + "type": "tableRow", + "content": [ + { + "type": "tableCell", + "content": [ + { "type": "text", "text": "Availability Classification" } + ] + }, + { + "type": "tableCell", + "content": [ + { "type": "text", "text": "Availability Requirements" } + ] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Scheduled Outage" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Recovery Time" }] + }, + { + "type": "tableCell", + "content": [ + { "type": "text", "text": "Data Loss or Impact Loss" } + ] + } + ] + }, + { + "type": "tableRow", + "content": [ + { + "type": "tableCell", + "content": [{ "type": "text", "text": "High" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "High to Continuous" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "30 minutes" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "1 hour" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Minimal" }] + } + ] + }, + { + "type": "tableRow", + "content": [ + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Medium" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Standard Availability" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "2 hours" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "4 hours" }] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Some data loss is tolerated if it results in quicker restoration" + } + ] + } + ] + }, + { + "type": "tableRow", + "content": [ + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Low" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Limited Availability" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "4 hours" }] + }, + { + "type": "tableCell", + "content": [{ "type": "text", "text": "Next business day" }] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Some data loss is tolerated if it results in quicker restoration" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "The business continuity plan must also ensure:" + } + ] + }, + { + "type": "orderedList", + "attrs": { "tight": true, "start": 1 }, + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Recovery time requirements and data loss limits must be adhered to with specific documentation in the plan." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Company and/or external critical resources, personnel, and necessary corrective actions must be specifically identified." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Specific responsibilities and tasks for responding to emergencies and resuming business operations must be included in the plan." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "All applicable legal and regulatory requirements must be satisfied." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Requirements for information system availability and redundancy are defined in the System Availability Policy." + } + ] + } + ] + } + ] + } + ] +} diff --git a/packages/data/policies/change.json b/packages/data/policies/change.json new file mode 100644 index 0000000000..6a64b8aa64 --- /dev/null +++ b/packages/data/policies/change.json @@ -0,0 +1,503 @@ +{ + "type": "doc", + "metadata": { + "id": "system-change-policy", + "slug": "system-change-policy", + "name": "System Change Policy", + "description": "This policy outlines the requirements for system changes.", + "usedBy": { + "soc2": ["CC3.4", "CC6.8", "CC7.1", "A1.1"] + } + }, + "content": [ + { + "type": "heading", + "attrs": { + "level": 1 + }, + "content": [ + { + "type": "text", + "text": "System Change Policy" + } + ] + }, + { + "type": "heading", + "attrs": { + "level": 2 + }, + "content": [ + { + "type": "text", + "text": "Policy Information" + } + ] + }, + { + "type": "table", + "content": [ + { + "type": "tableRow", + "content": [ + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Organization" + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Last Review" + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Review Frequency" + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Approved By" + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Classification" + } + ] + } + ] + }, + { + "type": "tableRow", + "content": [ + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "{{organization}}" + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "{{date}}" + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Annual" + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Chief Information Security Officer" + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Confidential" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { + "level": 2 + }, + "content": [ + { + "type": "text", + "text": "Purpose and Scope" + } + ] + }, + { + "type": "orderedList", + "attrs": { + "tight": true, + "start": 1 + }, + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "This information security policy defines how changes to information systems are planned and implemented." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "This policy applies to the entire information security program at the organization (i.e. to all information and communications technology, as well as related documentation)." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "All employees, contractors, part-time and temporary workers, service providers, and those employed by others to perform work for the organization, or who have been granted to the organization's information and communications technology, must comply with this policy." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { + "level": 2 + }, + "content": [ + { + "type": "text", + "text": "Background" + } + ] + }, + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "This policy defines specific requirements to ensure that changes to systems and applications are properly planned, evaluated, reviewed, approved, communicated, implemented, documented, and reviewed, thereby ensuring the greatest probability of success. Where changes are not successful, this document provides mechanisms for conducting post-implementation review such that future mistakes and errors can be prevented." + } + ] + }, + { + "type": "heading", + "attrs": { + "level": 2 + }, + "content": [ + { + "type": "text", + "text": "Policy" + } + ] + }, + { + "type": "orderedList", + "attrs": { + "tight": true, + "start": 1 + }, + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Any changes to the security architecture or customer data handling of a system must be formally requested in writing to the organization's Information Security Manager (ISM), and approved by the ISM and the Chief Information Officer (CIO)." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "All change requests must be documented." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "All change requests must be prioritized in terms of benefits, urgency, effort required, and potential impacts to the organization's operations." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "All implemented changes must be communicated to relevant users." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Change management must be conducted according to the following procedure:" + } + ] + }, + { + "type": "orderedList", + "attrs": { + "tight": true, + "start": 1 + }, + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": " Planning" + }, + { + "type": "text", + "text": ": " + }, + { + "type": "text", + "text": "plan the change, including the implementation design, scheduling, and implementation of a communications plan, testing plan, and roll-back plan." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": " Evaluation" + }, + { + "type": "text", + "text": ": " + }, + { + "type": "text", + "text": "evaluate the change, including priority level of the service and risk that the proposed change introduces to the system; determine the change type and the specific step-by-step process to implement the change." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": " Review" + }, + { + "type": "text", + "text": ": " + }, + { + "type": "text", + "text": "review the change plan amongst the CIO, ISM, Engineering Lead, and, if applicable, Business Unit Manager." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": " Approval" + }, + { + "type": "text", + "text": ": " + }, + { + "type": "text", + "text": "the CIO must approve the change plan." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": " Communication" + }, + { + "type": "text", + "text": ": " + }, + { + "type": "text", + "text": "communicate the change to all users of the system." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": " Implementation" + }, + { + "type": "text", + "text": ": " + }, + { + "type": "text", + "text": "test and implement the change." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": " Documentation" + }, + { + "type": "text", + "text": ": " + }, + { + "type": "text", + "text": "record the change and any post-implementation issues." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": " Post-change review" + }, + { + "type": "text", + "text": ": " + }, + { + "type": "text", + "text": "conduct a post-implementation review to determine how the change is impacting the organization, either positively or negatively. Discuss and document any lessons learned." + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] +} diff --git a/packages/data/policies/classification.json b/packages/data/policies/classification.json new file mode 100644 index 0000000000..d67704b4c9 --- /dev/null +++ b/packages/data/policies/classification.json @@ -0,0 +1,770 @@ +{ + "type": "doc", + "metadata": { + "id": "data-classification-policy", + "slug": "data-classification-policy", + "name": "Data Classification Policy", + "description": "This policy outlines the requirements for data classification.", + "usedBy": { + "soc2": ["CC6.1", "CC8.1", "CC6.6"] + } + }, + "content": [ + { + "type": "heading", + "attrs": { + "level": 1 + }, + "content": [ + { + "type": "text", + "text": "Data Classification Policy" + } + ] + }, + { + "type": "heading", + "attrs": { + "level": 2 + }, + "content": [ + { + "type": "text", + "text": "Policy Information" + } + ] + }, + { + "type": "table", + "content": [ + { + "type": "tableRow", + "content": [ + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Organization" + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Last Review" + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Review Frequency" + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Approved By" + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Classification" + } + ] + } + ] + }, + { + "type": "tableRow", + "content": [ + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "{{organization}}" + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "{{date}}" + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Annual" + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Chief Information Security Officer" + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Confidential" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { + "level": 2 + }, + "content": [ + { + "type": "text", + "text": "Purpose and Scope" + } + ] + }, + { + "type": "orderedList", + "attrs": { + "tight": true, + "start": 1 + }, + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "This data classification policy defines the requirements to ensure that information within the organization is protected at an appropriate level." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "This document applies to the entire scope of the organization's information security program. It includes all types of information, regardless of its form, such as paper or electronic documents, applications and databases, and knowledge or information that is not written." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "This policy applies to all individuals and systems that have access to information kept by the organization." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { + "level": 2 + }, + "content": [ + { + "type": "text", + "text": "Background" + } + ] + }, + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "This policy defines the high level objectives and implementation instructions for the organization's data classification scheme. This includes data classification levels, as well as procedures for the classification, labeling and handling of data within the organization. Confidentiality and non-disclosure agreements maintained by the organization must reference this policy." + } + ] + }, + { + "type": "heading", + "attrs": { + "level": 2 + }, + "content": [ + { + "type": "text", + "text": "Classification Levels" + } + ] + }, + { + "type": "table", + "content": [ + { + "type": "tableRow", + "content": [ + { + "type": "tableCell", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "marks": [ + { + "type": "bold" + } + ], + "text": "Confidentiality Level" + } + ] + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "marks": [ + { + "type": "bold" + } + ], + "text": "Label" + } + ] + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "marks": [ + { + "type": "bold" + } + ], + "text": "Classification Criteria" + } + ] + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "marks": [ + { + "type": "bold" + } + ], + "text": "Access Restrictions" + } + ] + } + ] + } + ] + }, + { + "type": "tableRow", + "content": [ + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Public" + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "For Public Release" + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Making the information public will not harm the organization in any way." + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Information is available to the public." + } + ] + } + ] + }, + { + "type": "tableRow", + "content": [ + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Internal Use" + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Internal Use" + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Unauthorized access may cause minor damage and/or inconvenience to the organization." + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Information is available to all employees and authorized third parties." + } + ] + } + ] + }, + { + "type": "tableRow", + "content": [ + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Restricted" + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Restricted" + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Unauthorized access to information may cause considerable damage to the business and/or the organization's reputation." + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Information is available to a specific group of employees and authorized third parties." + } + ] + } + ] + }, + { + "type": "tableRow", + "content": [ + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Confidential" + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Confidential" + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Unauthorized access to information may cause catastrophic damage to business and/or the organization's reputation." + } + ] + }, + { + "type": "tableCell", + "content": [ + { + "type": "text", + "text": "Information is available only to specific individuals in the organization." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { + "level": 2 + }, + "content": [ + { + "type": "text", + "text": "Policy" + } + ] + }, + { + "type": "orderedList", + "attrs": { + "tight": true, + "start": 1 + }, + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "If classified information is received from outside the organization, the person who receives the information must classify it in accordance with the rules prescribed in this policy. The person thereby will become the owner of the information." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "If classified information is received from outside the organization and handled as part of business operations activities (e.g., customer data on provided cloud services), the information classification, as well as the owner of such information, must be made in accordance with the specifications of the respective customer service agreement and other legal requirements." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "When classifying information, the level of confidentiality is determined by:" + } + ] + }, + { + "type": "orderedList", + "attrs": { + "tight": true, + "start": 1 + }, + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": " Value" + }, + { + "type": "text", + "text": ": " + }, + { + "type": "text", + "text": "The value of the information, based on impacts identified during the risk assessment process." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": " Sensitivity" + }, + { + "type": "text", + "text": ": " + }, + { + "type": "text", + "text": "Sensitivity and criticality of the information, based on the highest risk calculated for each information item during the risk assessment." + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": " Legal obligations" + }, + { + "type": "text", + "text": ": " + }, + { + "type": "text", + "text": "Legal, regulatory and contractual obligations." + } + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { + "level": 2 + }, + "content": [ + { + "type": "text", + "text": "Appendices" + } + ] + }, + { + "type": "heading", + "attrs": { + "level": 3 + }, + "content": [ + { + "type": "text", + "text": "Appendix A: Handling of Classified Information" + } + ] + }, + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Information and information systems must be handled according to detailed guidelines covering:" + } + ] + }, + { + "type": "bulletList", + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Paper Documents" + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Electronic Documents" + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Information Systems" + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Electronic Mail" + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Electronic Storage Media" + } + ] + } + ] + }, + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Information Transmitted Orally" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "attrs": { + "level": 3 + }, + "content": [ + { + "type": "text", + "text": "Appendix B: Form - Confidentiality Statement" + } + ] + }, + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "All persons accessing classified information must complete and submit a Confidentiality Statement to their immediate supervisor or company point-of-contact." + } + ] + } + ] +} diff --git a/packages/data/policies/password_policy.json b/packages/data/policies/password_policy.json index 200e51ec7c..b21b3cf2f0 100644 --- a/packages/data/policies/password_policy.json +++ b/packages/data/policies/password_policy.json @@ -1,12 +1,12 @@ { - "id": "password_policy", - "slug": "password-policy", - "name": "Password Policy", - "description": "This policy outlines the requirements for passwords used by employees.", - "template": "<html>...</html>", - "usedBy": { - "soc2": { - "CC1": ["CC1.1", "CC1.2", "CC1.3"] + "metadata": { + "id": "password_policy", + "slug": "password-policy", + "name": "Password Policy", + "description": "This policy outlines the requirements for passwords used by employees.", + "usedBy": { + "soc2": ["CC1.1", "CC1.2", "CC1.3"] } - } + }, + "content": [] } diff --git a/packages/db/prisma/migrations/20250211223842_policy_column_change_type/migration.sql b/packages/db/prisma/migrations/20250211223842_policy_column_change_type/migration.sql new file mode 100644 index 0000000000..cc70b2aafb --- /dev/null +++ b/packages/db/prisma/migrations/20250211223842_policy_column_change_type/migration.sql @@ -0,0 +1,19 @@ +/* + Warnings: + + - You are about to drop the column `template` on the `Policy` table. All the data in the column will be lost. + - A unique constraint covering the columns `[subdomain]` on the table `Organization` will be added. If there are existing duplicate values, this will fail. + +*/ +-- AlterTable +ALTER TABLE "Organization" ADD COLUMN "subdomain" TEXT NOT NULL DEFAULT 'example.com'; + +-- AlterTable +ALTER TABLE "Policy" DROP COLUMN "template", +ADD COLUMN "content" JSONB[]; + +-- AlterTable +ALTER TABLE "RiskMitigationTask" ADD COLUMN "notifiedAt" TIMESTAMP(3); + +-- CreateIndex +CREATE UNIQUE INDEX "Organization_subdomain_key" ON "Organization"("subdomain"); diff --git a/packages/db/prisma/migrations/20250211224712_policy_column_change/migration.sql b/packages/db/prisma/migrations/20250211224712_policy_column_change/migration.sql new file mode 100644 index 0000000000..33bc3b28f6 --- /dev/null +++ b/packages/db/prisma/migrations/20250211224712_policy_column_change/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Organization" ALTER COLUMN "subdomain" DROP DEFAULT; diff --git a/packages/db/prisma/migrations/20250212175319_add_content_to_organization_policy/migration.sql b/packages/db/prisma/migrations/20250212175319_add_content_to_organization_policy/migration.sql new file mode 100644 index 0000000000..a2de701f5b --- /dev/null +++ b/packages/db/prisma/migrations/20250212175319_add_content_to_organization_policy/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "OrganizationPolicy" ADD COLUMN "content" JSONB[]; diff --git a/packages/db/prisma/schema.prisma b/packages/db/prisma/schema.prisma index 8edc206955..df1be60768 100644 --- a/packages/db/prisma/schema.prisma +++ b/packages/db/prisma/schema.prisma @@ -844,7 +844,7 @@ model Policy { slug String @unique name String description String? - template String + content Json[] version String? usedBy Json createdAt DateTime @default(now()) @@ -945,6 +945,8 @@ model OrganizationPolicy { policyId String policy Policy @relation(fields: [policyId], references: [id], onDelete: Cascade) + content Json[] + @@unique([organizationId, policyId]) @@index([organizationId]) @@index([policyId]) diff --git a/packages/db/prisma/seed.ts b/packages/db/prisma/seed.ts index 3d1ab2b655..72cadebbbd 100644 --- a/packages/db/prisma/seed.ts +++ b/packages/db/prisma/seed.ts @@ -1,19 +1,30 @@ -import { PrismaClient, type RequirementType } from "@prisma/client"; +import { PrismaClient } from "@prisma/client"; +import type { RequirementType, Prisma } from "@prisma/client"; import { readFileSync, readdirSync } from "node:fs"; import { join } from "node:path"; import fs from "node:fs"; -import type { FrameworkCategory, Framework, Control } from "./seedTypes"; +import type { + FrameworkCategory, + Framework, + Control, + Policy, +} from "./seedTypes"; +import type { JsonValue } from "@prisma/client/runtime/library"; const prisma = new PrismaClient(); async function main() { console.log("\nšŸ—‘ļø Cleaning up existing data..."); // For testing, we will delete all existing data, except for the users. - await prisma.framework.deleteMany(); + await prisma.policyControl.deleteMany(); + await prisma.policyFramework.deleteMany(); + await prisma.controlRequirement.deleteMany(); + await prisma.control.deleteMany(); await prisma.frameworkCategory.deleteMany(); + await prisma.framework.deleteMany(); await prisma.policy.deleteMany(); - await prisma.control.deleteMany(); - await prisma.controlRequirement.deleteMany(); + await prisma.organizationPolicy.deleteMany(); + await prisma.organizationFramework.deleteMany(); console.log("āœ… Database cleaned"); console.log("\nšŸ“‹ Seeding policies..."); @@ -52,23 +63,23 @@ async function seedPolicies() { console.log(` ā³ Processing ${file}...`); const policyData = JSON.parse( readFileSync(join(policiesDir, file), "utf8") - ); + ) as Policy; await prisma.policy.upsert({ - where: { id: policyData.id }, + where: { id: policyData.metadata.id }, update: { - name: policyData.name, - description: policyData.description, - template: policyData.template, - usedBy: policyData.usedBy, + name: policyData.metadata.name, + description: policyData.metadata.description, + content: policyData.content as Prisma.InputJsonValue[], + usedBy: policyData.metadata.usedBy as Prisma.InputJsonValue, }, create: { - id: policyData.id, - slug: policyData.slug, - name: policyData.name, - description: policyData.description, - template: policyData.template, - usedBy: policyData.usedBy, + id: policyData.metadata.id, + slug: policyData.metadata.slug, + name: policyData.metadata.name, + description: policyData.metadata.description, + content: policyData.content as Prisma.InputJsonValue[], + usedBy: policyData.metadata.usedBy as Prisma.InputJsonValue, }, }); console.log(` āœ… ${file} processed`); @@ -194,6 +205,7 @@ async function seedFrameworkCategoryControls( frameworkCategoryId: categoryCode, }, create: { + // Use the control code (e.g. CC1.1) as both the id and code id: controlCode, code: controlCode, name: controlData.name, @@ -208,13 +220,27 @@ 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; + } + } + await prisma.controlRequirement.upsert({ where: { id: requirement.id, }, create: { id: requirement.id, - controlId: insertedControl.id, + controlId: controlCode, type: requirement.type as RequirementType, description: requirement.description, policyId: @@ -247,39 +273,53 @@ async function seedPolicyFramework() { continue; } - for (const [frameworkId, categories] of Object.entries( + for (const [frameworkId, controlCodes] of Object.entries( policy.usedBy as Record<string, string[]> )) { - // Upsert the policy framework mapping. + // First verify the framework exists + const framework = await prisma.framework.findUnique({ + where: { id: frameworkId }, + }); + + if (!framework) { + console.log(` āš ļø Framework ${frameworkId} not found, skipping`); + continue; + } + + // Upsert the policy framework mapping await prisma.policyFramework.upsert({ where: { id: `${frameworkId}_${policy.id}` }, update: { policyId: policy.id, - frameworkId: frameworkId as string, + frameworkId: frameworkId, }, create: { id: `${frameworkId}_${policy.id}`, policyId: policy.id, - frameworkId: frameworkId as string, + frameworkId: frameworkId, }, }); - for (const [categoryCode, controlCodes] of Object.entries(categories)) { - for (const controlCode of controlCodes) { - // Upsert the policy control mapping. - await prisma.policyControl.upsert({ - where: { id: `${frameworkId}_${categoryCode}_${controlCode}` }, - update: { - policyId: policy.id, - controlId: controlCode, - }, - create: { - id: `${frameworkId}_${categoryCode}_${controlCode}`, - policyId: policy.id, - controlId: controlCode, - }, - }); - } + // For each control code, create the policy control mapping directly + for (const controlCode of controlCodes) { + console.log( + ` ā³ Mapping control ${controlCode} to policy ${policy.name}` + ); + // Now create the policy control mapping using the control code directly + await prisma.policyControl.upsert({ + where: { + id: `${frameworkId}_${policy.id}_${controlCode}`, + }, + update: { + policyId: policy.id, + controlId: controlCode, // Use the control code directly + }, + create: { + id: `${frameworkId}_${policy.id}_${controlCode}`, + policyId: policy.id, + controlId: controlCode, // Use the control code directly + }, + }); } } console.log(` āœ… Policy ${policy.name} mapped`); diff --git a/packages/db/prisma/seedTypes.ts b/packages/db/prisma/seedTypes.ts index b8ae6fc25b..48faca844b 100644 --- a/packages/db/prisma/seedTypes.ts +++ b/packages/db/prisma/seedTypes.ts @@ -1,3 +1,5 @@ +import type { JsonArray } from "@prisma/client/runtime/library"; + export interface Framework { name: string; description: string; @@ -23,3 +25,17 @@ export interface ControlRequirement { description: string; policyId?: string; } + +export interface Policy { + metadata: { + id: string; + slug: string; + name: string; + description: string; + type: string; + usedBy: { + [key: string]: string[]; + }; + }; + content: JsonArray; +}