From 7321862ba9f2c8d2f4a57afc40a543c2abd90c07 Mon Sep 17 00:00:00 2001 From: gearnode Date: Thu, 27 Mar 2025 23:34:35 +0100 Subject: [PATCH] First step of mitigation migration Signed-off-by: gearnode --- apps/console/src/components/NavMain.tsx | 16 +- .../frameworks/FrameworkListView.tsx | 42 - .../frameworks/FrameworkView.tsx | 557 ++--- .../frameworks/UpdateFrameworkView.tsx | 3 - ...ListViewImportFrameworkMutation.graphql.ts | 83 +- .../FrameworkListViewQuery.graphql.ts | 88 +- .../FrameworkViewDeleteMutation.graphql.ts | 132 ++ .../FrameworkViewQuery.graphql.ts | 107 +- .../UpdateFrameworkViewQuery.graphql.ts | 22 +- ...workViewUpdateFrameworkMutation.graphql.ts | 17 +- .../frameworks/mitigations/MitigationView.tsx | 35 +- .../mitigations/UpdateMitigationView.tsx | 4 - ...ionViewCreateMitigationMutation.graphql.ts | 4 +- ...itigationViewAssignTaskMutation.graphql.ts | 16 +- ...itigationViewCreateTaskMutation.graphql.ts | 16 +- .../MitigationViewQuery.graphql.ts | 79 +- ...igationViewUnassignTaskMutation.graphql.ts | 16 +- ...ewUpdateMitigationStateMutation.graphql.ts | 17 +- ...tionViewUpdateTaskStateMutation.graphql.ts | 17 +- .../UpdateMitigationViewQuery.graphql.ts | 22 +- ...ionViewUpdateMitigationMutation.graphql.ts | 17 +- .../pages/organizations/people/PeopleView.tsx | 5 +- .../__generated__/PeopleViewQuery.graphql.ts | 22 +- .../PeopleViewUpdatePeopleMutation.graphql.ts | 17 +- .../policies/UpdatePolicyView.tsx | 3 - .../UpdatePolicyViewMutation.graphql.ts | 17 +- .../UpdatePolicyViewQuery.graphql.ts | 16 +- .../organizations/vendors/VendorView.tsx | 5 +- .../__generated__/VendorViewQuery.graphql.ts | 22 +- .../VendorViewUpdateVendorMutation.graphql.ts | 17 +- data/frameworks/ISO27001-2022.json | 620 ++++++ data/frameworks/SOC2.json | 249 +++ data/frameworks/soc2.yaml | 365 --- pkg/coredata/control.go | 262 +++ pkg/coredata/control_mitigation.go | 168 ++ pkg/coredata/control_order_field.go | 40 + pkg/coredata/entity_type_reg.go | 1 + pkg/coredata/framework.go | 94 +- pkg/coredata/migrations/20250327T122105Z.sql | 18 + pkg/coredata/migrations/20250327T122609Z.sql | 8 + pkg/coredata/migrations/20250327T210900Z.sql | 1 + pkg/coredata/migrations/20250327T212900Z.sql | 2 + pkg/coredata/migrations/20250327T220600Z.sql | 1 + pkg/coredata/migrations/20250327T220601Z.sql | 1 + pkg/coredata/migrations/20250607T120000Z.sql | 19 + pkg/coredata/mitigation.go | 72 +- pkg/page/cursor_key.go | 6 + pkg/probo/control_service.go | 282 +++ pkg/probo/framework_service.go | 125 +- pkg/probo/mitigation_service.go | 43 +- pkg/probo/service.go | 19 +- pkg/server/api/console/v1/schema.graphql | 78 +- pkg/server/api/console/v1/schema/schema.go | 1953 ++++++++++++----- pkg/server/api/console/v1/types/control.go | 55 + pkg/server/api/console/v1/types/framework.go | 1 - pkg/server/api/console/v1/types/mitigation.go | 1 - pkg/server/api/console/v1/types/people.go | 1 - pkg/server/api/console/v1/types/policy.go | 1 - pkg/server/api/console/v1/types/task.go | 1 - pkg/server/api/console/v1/types/types.go | 125 +- pkg/server/api/console/v1/types/vendor.go | 1 - pkg/server/api/console/v1/v1_resolver.go | 119 +- 62 files changed, 4025 insertions(+), 2141 deletions(-) create mode 100644 apps/console/src/pages/organizations/frameworks/__generated__/FrameworkViewDeleteMutation.graphql.ts create mode 100644 data/frameworks/ISO27001-2022.json create mode 100644 data/frameworks/SOC2.json delete mode 100644 data/frameworks/soc2.yaml create mode 100644 pkg/coredata/control.go create mode 100644 pkg/coredata/control_mitigation.go create mode 100644 pkg/coredata/control_order_field.go create mode 100644 pkg/coredata/migrations/20250327T122105Z.sql create mode 100644 pkg/coredata/migrations/20250327T122609Z.sql create mode 100644 pkg/coredata/migrations/20250327T210900Z.sql create mode 100644 pkg/coredata/migrations/20250327T212900Z.sql create mode 100644 pkg/coredata/migrations/20250327T220600Z.sql create mode 100644 pkg/coredata/migrations/20250327T220601Z.sql create mode 100644 pkg/coredata/migrations/20250607T120000Z.sql create mode 100644 pkg/probo/control_service.go create mode 100644 pkg/server/api/console/v1/types/control.go diff --git a/apps/console/src/components/NavMain.tsx b/apps/console/src/components/NavMain.tsx index 467a6c431..63d7155ca 100644 --- a/apps/console/src/components/NavMain.tsx +++ b/apps/console/src/components/NavMain.tsx @@ -1,6 +1,12 @@ "use client"; -import { ChevronRight, type LucideIcon } from "lucide-react"; +import { + ChevronRight, + ClipboardList, + ListCheck, + NotebookTabs, + type LucideIcon, +} from "lucide-react"; import { Link, useLocation, useParams } from "react-router"; import { @@ -138,8 +144,14 @@ export function NavMain() { } function getNavItems(organizationId?: string): NavItem[] { - // Always return the same structure, but with or without URLs depending on whether an organization is selected return [ + { + title: "Mitigations", + icon: ClipboardList, + url: organizationId + ? `/organizations/${organizationId}/mitigations` + : undefined, + }, { title: "Frameworks", url: organizationId diff --git a/apps/console/src/pages/organizations/frameworks/FrameworkListView.tsx b/apps/console/src/pages/organizations/frameworks/FrameworkListView.tsx index 8a1ec9fe1..457ff36cf 100644 --- a/apps/console/src/pages/organizations/frameworks/FrameworkListView.tsx +++ b/apps/console/src/pages/organizations/frameworks/FrameworkListView.tsx @@ -37,14 +37,6 @@ const FrameworkListViewQuery = graphql` id name description - mitigations(first: 100) { - edges { - node { - id - state - } - } - } createdAt updatedAt } @@ -66,14 +58,6 @@ const FrameworkListViewImportFrameworkMutation = graphql` id name description - mitigations(first: 100) { - edges { - node { - id - state - } - } - } createdAt updatedAt } @@ -86,14 +70,10 @@ function FrameworkCard({ title, description, icon, - status, - progress, }: { title: string; description: string; icon: React.ReactNode; - status?: string; - progress?: string; }) { return ( @@ -111,13 +91,6 @@ function FrameworkCard({

{description}

- - {progress && ( -
- - {progress} -
- )}
); @@ -240,11 +213,6 @@ function FrameworkListViewContent({
{frameworks.map((framework) => { - const validatedControls = framework.mitigations.edges.filter( - (edge) => edge?.node?.state === "IMPLEMENTED" - ).length; - const totalControls = framework.mitigations.edges.length; - return (
} - status={ - validatedControls === totalControls - ? "Compliant" - : undefined - } - progress={ - validatedControls === totalControls - ? "All mitigations validated" - : `${validatedControls}/${totalControls} Controls validated` - } /> ); diff --git a/apps/console/src/pages/organizations/frameworks/FrameworkView.tsx b/apps/console/src/pages/organizations/frameworks/FrameworkView.tsx index fa3867a9b..e0aaf9698 100644 --- a/apps/console/src/pages/organizations/frameworks/FrameworkView.tsx +++ b/apps/console/src/pages/organizations/frameworks/FrameworkView.tsx @@ -1,24 +1,27 @@ -import { Suspense, useEffect, useState } from "react"; +import { Suspense, useEffect, useState, useCallback } from "react"; import { useParams, useNavigate, Link } from "react-router"; import { graphql, PreloadedQuery, usePreloadedQuery, useQueryLoader, + useMutation, + ConnectionHandler, } from "react-relay"; -import { - AlertCircle, - CheckCircle2, - ChevronDown, - ChevronRight, - Clock, - Plus, - X, -} from "lucide-react"; +import { Plus } from "lucide-react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; -import { Badge } from "@/components/ui/badge"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { useToast } from "@/hooks/use-toast"; import type { FrameworkViewQuery as FrameworkViewQueryType } from "./__generated__/FrameworkViewQuery.graphql"; +import type { FrameworkViewDeleteMutation } from "./__generated__/FrameworkViewDeleteMutation.graphql"; import { PageTemplate } from "@/components/PageTemplate"; import { FrameworkViewSkeleton } from "./FrameworkPage"; @@ -29,15 +32,14 @@ const FrameworkViewQuery = graphql` ... on Framework { name description - mitigations(first: 100) @connection(key: "FrameworkView_mitigations") { + controls(first: 100, orderBy: { field: CREATED_AT, direction: ASC }) + @connection(key: "FrameworkView_controls") { edges { node { id + referenceId name description - state - category - importance } } } @@ -46,25 +48,16 @@ const FrameworkViewQuery = graphql` } `; -interface Mitigation { - id?: string; - name?: string; - description?: string; - state?: string; - category?: string; - importance?: string; - status?: string; -} - -interface Category { - id: string; - name: string; - description: string; - progress: number; - mitigations: Mitigation[]; - doneCount: number; - totalCount: number; -} +const DeleteFrameworkMutation = graphql` + mutation FrameworkViewDeleteMutation( + $input: DeleteFrameworkInput! + $connections: [ID!]! + ) { + deleteFramework(input: $input) { + deletedFrameworkId @deleteEdge(connections: $connections) + } + } +`; function FrameworkViewContent({ queryRef, @@ -73,158 +66,73 @@ function FrameworkViewContent({ }) { const data = usePreloadedQuery(FrameworkViewQuery, queryRef); const framework = data.node; - const mitigations = - framework.mitigations?.edges.map((edge) => edge?.node) ?? []; const navigate = useNavigate(); const { organizationId } = useParams(); + const { toast } = useToast(); + const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); + const [isDeleting, setIsDeleting] = useState(false); - // Monitor URL hash for changes and update state accordingly - const [hashValue, setHashValue] = useState(window.location.hash); + // Extract controls from the GraphQL response + const controls = framework?.controls?.edges?.map((edge) => edge.node) || []; - // Get the active category from the hash (used when returning from a mitigation) - const hashCategory = hashValue.substring(1) - ? decodeURIComponent(hashValue.substring(1)) - : ""; + // Setup delete mutation + const [commitDeleteMutation] = useMutation( + DeleteFrameworkMutation + ); - // Keep track of manually expanded categories - const [expandedCategories, setExpandedCategories] = useState(() => { - return hashCategory ? [hashCategory] : []; - }); + const handleDeleteFramework = useCallback(() => { + setIsDeleting(true); - // When hash changes, update expanded categories to include the hash category - useEffect(() => { - if (hashCategory && !expandedCategories.includes(hashCategory)) { - setExpandedCategories((prev) => [...prev, hashCategory]); - } - }, [hashCategory, expandedCategories]); + const connectionId = ConnectionHandler.getConnectionID( + organizationId!, + "FrameworkListView_frameworks" + ); - // Listen for hash changes (like when using back button) - useEffect(() => { - const handleHashChange = () => { - setHashValue(window.location.hash); - }; + commitDeleteMutation({ + variables: { + input: { + frameworkId: framework.id, + }, + connections: [connectionId], + }, + onCompleted: (_, errors) => { + setIsDeleting(false); + setIsDeleteDialogOpen(false); - window.addEventListener("hashchange", handleHashChange); + if (errors) { + console.error("Error deleting framework:", errors); + toast({ + title: "Error", + description: "Failed to delete framework. Please try again.", + variant: "destructive", + }); + return; + } - return () => { - window.removeEventListener("hashchange", handleHashChange); - }; - }, []); + toast({ + title: "Success", + description: "Framework deleted successfully.", + }); - // Map mitigation state to status for the new design - const mapStateToStatus = (state?: string): string => { - if (!state) return "incomplete"; - switch (state) { - case "IMPLEMENTED": - return "complete"; - case "NOT_APPLICABLE": - return "not-applicable"; - case "NOT_STARTED": - return "not-started"; - default: - return "in-progress"; - } - }; - - const processedControls = mitigations.map((mitigation) => ({ - ...mitigation, - status: mapStateToStatus(mitigation.state), - })); - - // Calculate global progress - const implementedCount = processedControls.filter( - (mitigation) => mitigation.status === "complete" - ).length; - const notApplicableCount = processedControls.filter( - (mitigation) => mitigation.status === "not-applicable" - ).length; - const totalControls = processedControls.length; - - // Include not-applicable as effectively "complete" for progress percentage - const effectiveCompletedCount = implementedCount + notApplicableCount; - const globalProgress = totalControls - ? Math.round((effectiveCompletedCount / totalControls) * 100) - : 0; - - // Get global status counts - const globalStatusCounts = processedControls.reduce((acc, mitigation) => { - if (mitigation.status) { - acc[mitigation.status] = (acc[mitigation.status] || 0) + 1; - } - return acc; - }, {} as Record); - - // Group mitigations by category - const controlsByCategory = processedControls.reduce((acc, mitigation) => { - if (!mitigation?.category) return acc; - if (!acc[mitigation.category]) { - acc[mitigation.category] = []; - } - acc[mitigation.category].push(mitigation); - return acc; - }, {} as Record); - - // Function to toggle a category's expanded state - now supports multiple expanded categories - const toggleCategory = (categoryId: string) => { - setExpandedCategories((prev) => { - if (prev.includes(categoryId)) { - return prev.filter((id) => id !== categoryId); - } else { - return [...prev, categoryId]; - } + navigate(`/organizations/${organizationId}/frameworks`); + }, + onError: (error) => { + setIsDeleting(false); + setIsDeleteDialogOpen(false); + console.error("Error deleting framework:", error); + toast({ + title: "Error", + description: "Failed to delete framework. Please try again.", + variant: "destructive", + }); + }, }); - }; - - const categories: Category[] = Object.entries(controlsByCategory) - .map(([categoryName, categoryControls]) => { - const catImplementedCount = categoryControls.filter( - (mitigation) => mitigation.status === "complete" - ).length; - const catNotApplicableCount = categoryControls.filter( - (mitigation) => mitigation.status === "not-applicable" - ).length; - // Consider both "complete" and "not-applicable" as done for category progress - const catDoneCount = catImplementedCount + catNotApplicableCount; - const catTotalCount = categoryControls.length; - const progress = catTotalCount - ? Math.round((catDoneCount / catTotalCount) * 100) - : 0; - - return { - id: categoryName, - name: categoryName, - description: `Controls related to ${categoryName.toLowerCase()}`, - progress: progress, - mitigations: categoryControls.sort((a, b) => - (a.name || "").localeCompare(b.name || "") - ), - doneCount: catDoneCount, - totalCount: catTotalCount, - }; - }) - .filter((category) => category.mitigations.length > 0) - .sort((a, b) => a.name.localeCompare(b.name)); - - const getStatusIcon = (status: string) => { - switch (status) { - case "complete": - return ; - case "in-progress": - return ; - case "not-started": - return ; - case "incomplete": - return ; - case "not-applicable": - return ; - default: - return null; - } - }; + }, [framework.id, organizationId, commitDeleteMutation, navigate, toast]); return ( -
} > - {/* Global Progress Summary */} -
-
-

Framework Implementation

- - {globalProgress}% complete - -
- - {/* Progress bar container */} -
- {/* Segmented progress bar */} -
- {/* Complete segment */} - {globalStatusCounts.complete > 0 && ( -
- )} - {/* In-progress segment */} - {globalStatusCounts["in-progress"] > 0 && ( -
- )} - {/* Incomplete segment */} - {globalStatusCounts.incomplete > 0 && ( -
- )} - {/* Not applicable segment */} - {globalStatusCounts["not-applicable"] > 0 && ( -
- )} - {/* Not started segment */} - {globalStatusCounts["not-started"] > 0 && ( -
- )} -
-
- - {/* Status legend - reorder to match progress bar */} -
- {globalStatusCounts.complete > 0 && ( -
-
- Complete ({globalStatusCounts.complete}) -
- )} - {globalStatusCounts["in-progress"] > 0 && ( -
-
- In Progress ({globalStatusCounts["in-progress"]}) -
- )} - {globalStatusCounts.incomplete > 0 && ( -
-
- Incomplete ({globalStatusCounts.incomplete}) -
- )} - {globalStatusCounts["not-applicable"] > 0 && ( -
-
- - Not Applicable ({globalStatusCounts["not-applicable"]}) - -
- )} - {globalStatusCounts["not-started"] > 0 && ( -
-
- Not Started ({globalStatusCounts["not-started"]}) -
- )} -
-
-
- {categories.map((category) => { - const isExpanded = expandedCategories.includes(category.id); - - return ( -
- - toggleCategory(category.id)} + {controls.length > 0 ? ( + controls.map((control) => ( + + +
{ + navigate( + `/organizations/${organizationId}/frameworks/${framework.id}/controls/${control.id}` + ); + }} > -
-
- {category.name} -
-
- - {category.doneCount} / {category.totalCount} - - {isExpanded ? ( - - ) : ( - - )} -
+ + {control.referenceId} + + {control.name} +
+
+ {control.description} +
+ + +

Mitigations

+
+

+ Mitigations will be displayed here once connected to this + control +

+
+
- - - {isExpanded && ( - - {category.mitigations.length > 0 ? ( -
- - - - - - - - - - {category.mitigations.map((mitigation) => ( - { - if (mitigation?.id) { - // Always store just this category in the hash - // This is what will be expanded when returning - const encoded = encodeURIComponent( - category.id - ); - window.location.hash = encoded; - setHashValue("#" + encoded); - - // Make sure this category is expanded in the local state as well - if ( - !expandedCategories.includes(category.id) - ) { - setExpandedCategories((prev) => [ - ...prev, - category.id, - ]); - } - - // Use a small timeout to ensure the hash change is processed by the browser - setTimeout(() => { - navigate( - `/organizations/${organizationId}/frameworks/${framework.id}/mitigations/${mitigation.id}` - ); - }, 100); - } - }} - > - - - - - ))} - -
- Importance - - Status - - Mitigation -
- - {mitigation.importance} - - -
- {mitigation.status - ? getStatusIcon(mitigation.status) - : null} -
-
-
- {mitigation.name} -
-
-
- ) : ( -
- No mitigations in this category -
- )} -
- )} - -
- ); - })} +
+ +
+ )) + ) : ( +
+ No controls available for this framework +
+ )}
+ + {/* Delete Confirmation Dialog */} + + + + Delete Framework + + Are you sure you want to delete the framework " + {framework.name}"? This action cannot be undone. + + + + + + + + ); } diff --git a/apps/console/src/pages/organizations/frameworks/UpdateFrameworkView.tsx b/apps/console/src/pages/organizations/frameworks/UpdateFrameworkView.tsx index dccc75455..029d2a37d 100644 --- a/apps/console/src/pages/organizations/frameworks/UpdateFrameworkView.tsx +++ b/apps/console/src/pages/organizations/frameworks/UpdateFrameworkView.tsx @@ -29,7 +29,6 @@ const updateFrameworkMutation = graphql` id name description - version } } } @@ -42,7 +41,6 @@ const updateFrameworkQuery = graphql` id name description - version } } } @@ -166,7 +164,6 @@ function UpdateFrameworkViewContent({ variables: { input: { id: frameworkId!, - expectedVersion: data.node.version!, name: formData.name, description: formData.description, }, diff --git a/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkListViewImportFrameworkMutation.graphql.ts b/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkListViewImportFrameworkMutation.graphql.ts index d5f774ea5..274f8bb2f 100644 --- a/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkListViewImportFrameworkMutation.graphql.ts +++ b/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkListViewImportFrameworkMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<11936aad3da2565a509b1566774e0ecf>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -9,7 +9,6 @@ // @ts-nocheck import { ConcreteRequest } from 'relay-runtime'; -export type MitigationState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | "NOT_STARTED"; export type ImportFrameworkInput = { file: any; organizationId: string; @@ -25,14 +24,6 @@ export type FrameworkListViewImportFrameworkMutation$data = { readonly createdAt: string; readonly description: string; readonly id: string; - readonly mitigations: { - readonly edges: ReadonlyArray<{ - readonly node: { - readonly id: string; - readonly state: MitigationState; - }; - }>; - }; readonly name: string; readonly updatedAt: string; }; @@ -63,13 +54,6 @@ v2 = [ } ], v3 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "id", - "storageKey": null -}, -v4 = { "alias": null, "args": null, "concreteType": "FrameworkEdge", @@ -85,7 +69,13 @@ v4 = { "name": "node", "plural": false, "selections": [ - (v3/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, { "alias": null, "args": null, @@ -100,53 +90,6 @@ v4 = { "name": "description", "storageKey": null }, - { - "alias": null, - "args": [ - { - "kind": "Literal", - "name": "first", - "value": 100 - } - ], - "concreteType": "MitigationConnection", - "kind": "LinkedField", - "name": "mitigations", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "MitigationEdge", - "kind": "LinkedField", - "name": "edges", - "plural": true, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "Mitigation", - "kind": "LinkedField", - "name": "node", - "plural": false, - "selections": [ - (v3/*: any*/), - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "state", - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": "mitigations(first:100)" - }, { "alias": null, "args": null, @@ -185,7 +128,7 @@ return { "name": "importFramework", "plural": false, "selections": [ - (v4/*: any*/) + (v3/*: any*/) ], "storageKey": null } @@ -210,7 +153,7 @@ return { "name": "importFramework", "plural": false, "selections": [ - (v4/*: any*/), + (v3/*: any*/), { "alias": null, "args": null, @@ -233,16 +176,16 @@ return { ] }, "params": { - "cacheID": "d2d34f8e054d753d956120ea14791f79", + "cacheID": "0d952e4f9f3e106ea7a30d69f7268385", "id": null, "metadata": {}, "name": "FrameworkListViewImportFrameworkMutation", "operationKind": "mutation", - "text": "mutation FrameworkListViewImportFrameworkMutation(\n $input: ImportFrameworkInput!\n) {\n importFramework(input: $input) {\n frameworkEdge {\n node {\n id\n name\n description\n mitigations(first: 100) {\n edges {\n node {\n id\n state\n }\n }\n }\n createdAt\n updatedAt\n }\n }\n }\n}\n" + "text": "mutation FrameworkListViewImportFrameworkMutation(\n $input: ImportFrameworkInput!\n) {\n importFramework(input: $input) {\n frameworkEdge {\n node {\n id\n name\n description\n createdAt\n updatedAt\n }\n }\n }\n}\n" } }; })(); -(node as any).hash = "a812db41b10d16c25959a5a733fe6088"; +(node as any).hash = "8be3328101831be07eeea8670d47debd"; export default node; diff --git a/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkListViewQuery.graphql.ts b/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkListViewQuery.graphql.ts index bdc45c71f..7dd306360 100644 --- a/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkListViewQuery.graphql.ts +++ b/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkListViewQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<6b6bb27f3ae9e9cf720be1c3655c595e>> + * @generated SignedSource<<1d45b549aa04b80f7e0a2951d7910a3f>> * @lightSyntaxTransform * @nogrep */ @@ -9,7 +9,6 @@ // @ts-nocheck import { ConcreteRequest } from 'relay-runtime'; -export type MitigationState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | "NOT_STARTED"; export type FrameworkListViewQuery$variables = { organizationId: string; }; @@ -21,14 +20,6 @@ export type FrameworkListViewQuery$data = { readonly createdAt: string; readonly description: string; readonly id: string; - readonly mitigations: { - readonly edges: ReadonlyArray<{ - readonly node: { - readonly id: string; - readonly state: MitigationState; - }; - }>; - }; readonly name: string; readonly updatedAt: string; }; @@ -63,21 +54,14 @@ v2 = { "name": "id", "storageKey": null }, -v3 = [ - { - "kind": "Literal", - "name": "first", - "value": 100 - } -], -v4 = { +v3 = { "alias": null, "args": null, "kind": "ScalarField", "name": "__typename", "storageKey": null }, -v5 = [ +v4 = [ { "alias": null, "args": null, @@ -109,47 +93,6 @@ v5 = [ "name": "description", "storageKey": null }, - { - "alias": null, - "args": (v3/*: any*/), - "concreteType": "MitigationConnection", - "kind": "LinkedField", - "name": "mitigations", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "MitigationEdge", - "kind": "LinkedField", - "name": "edges", - "plural": true, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "Mitigation", - "kind": "LinkedField", - "name": "node", - "plural": false, - "selections": [ - (v2/*: any*/), - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "state", - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": "mitigations(first:100)" - }, { "alias": null, "args": null, @@ -164,7 +107,7 @@ v5 = [ "name": "updatedAt", "storageKey": null }, - (v4/*: any*/) + (v3/*: any*/) ], "storageKey": null }, @@ -203,6 +146,13 @@ v5 = [ ], "storageKey": null } +], +v5 = [ + { + "kind": "Literal", + "name": "first", + "value": 100 + } ]; return { "fragment": { @@ -229,7 +179,7 @@ return { "kind": "LinkedField", "name": "__FrameworkListView_frameworks_connection", "plural": false, - "selections": (v5/*: any*/), + "selections": (v4/*: any*/), "storageKey": null } ], @@ -257,23 +207,23 @@ return { "name": "node", "plural": false, "selections": [ - (v4/*: any*/), + (v3/*: any*/), { "kind": "InlineFragment", "selections": [ { "alias": null, - "args": (v3/*: any*/), + "args": (v5/*: any*/), "concreteType": "FrameworkConnection", "kind": "LinkedField", "name": "frameworks", "plural": false, - "selections": (v5/*: any*/), + "selections": (v4/*: any*/), "storageKey": "frameworks(first:100)" }, { "alias": null, - "args": (v3/*: any*/), + "args": (v5/*: any*/), "filters": null, "handle": "connection", "key": "FrameworkListView_frameworks", @@ -291,7 +241,7 @@ return { ] }, "params": { - "cacheID": "e5d4da21773770d146fb7d91e435caef", + "cacheID": "ad629b287bbe0482ef250bae68c4018c", "id": null, "metadata": { "connection": [ @@ -308,11 +258,11 @@ return { }, "name": "FrameworkListViewQuery", "operationKind": "query", - "text": "query FrameworkListViewQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n frameworks(first: 100) {\n edges {\n node {\n id\n name\n description\n mitigations(first: 100) {\n edges {\n node {\n id\n state\n }\n }\n }\n createdAt\n updatedAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n id\n }\n}\n" + "text": "query FrameworkListViewQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n frameworks(first: 100) {\n edges {\n node {\n id\n name\n description\n createdAt\n updatedAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n id\n }\n}\n" } }; })(); -(node as any).hash = "8da147529de5e229e38b836bd584e484"; +(node as any).hash = "27aa68a4b7303c125c427acdf6aa9dc4"; export default node; diff --git a/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkViewDeleteMutation.graphql.ts b/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkViewDeleteMutation.graphql.ts new file mode 100644 index 000000000..c3435e149 --- /dev/null +++ b/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkViewDeleteMutation.graphql.ts @@ -0,0 +1,132 @@ +/** + * @generated SignedSource<<9f35422e7d027485b106e6fb8879d460>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +export type DeleteFrameworkInput = { + frameworkId: string; +}; +export type FrameworkViewDeleteMutation$variables = { + connections: ReadonlyArray; + input: DeleteFrameworkInput; +}; +export type FrameworkViewDeleteMutation$data = { + readonly deleteFramework: { + readonly deletedFrameworkId: string; + }; +}; +export type FrameworkViewDeleteMutation = { + response: FrameworkViewDeleteMutation$data; + variables: FrameworkViewDeleteMutation$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = { + "defaultValue": null, + "kind": "LocalArgument", + "name": "connections" +}, +v1 = { + "defaultValue": null, + "kind": "LocalArgument", + "name": "input" +}, +v2 = [ + { + "kind": "Variable", + "name": "input", + "variableName": "input" + } +], +v3 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "deletedFrameworkId", + "storageKey": null +}; +return { + "fragment": { + "argumentDefinitions": [ + (v0/*: any*/), + (v1/*: any*/) + ], + "kind": "Fragment", + "metadata": null, + "name": "FrameworkViewDeleteMutation", + "selections": [ + { + "alias": null, + "args": (v2/*: any*/), + "concreteType": "DeleteFrameworkPayload", + "kind": "LinkedField", + "name": "deleteFramework", + "plural": false, + "selections": [ + (v3/*: any*/) + ], + "storageKey": null + } + ], + "type": "Mutation", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [ + (v1/*: any*/), + (v0/*: any*/) + ], + "kind": "Operation", + "name": "FrameworkViewDeleteMutation", + "selections": [ + { + "alias": null, + "args": (v2/*: any*/), + "concreteType": "DeleteFrameworkPayload", + "kind": "LinkedField", + "name": "deleteFramework", + "plural": false, + "selections": [ + (v3/*: any*/), + { + "alias": null, + "args": null, + "filters": null, + "handle": "deleteEdge", + "key": "", + "kind": "ScalarHandle", + "name": "deletedFrameworkId", + "handleArgs": [ + { + "kind": "Variable", + "name": "connections", + "variableName": "connections" + } + ] + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "b10bac4b295c5d2a9a564cbba4569633", + "id": null, + "metadata": {}, + "name": "FrameworkViewDeleteMutation", + "operationKind": "mutation", + "text": "mutation FrameworkViewDeleteMutation(\n $input: DeleteFrameworkInput!\n) {\n deleteFramework(input: $input) {\n deletedFrameworkId\n }\n}\n" + } +}; +})(); + +(node as any).hash = "8b20886821651ffb7e335df58fb46fc9"; + +export default node; diff --git a/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkViewQuery.graphql.ts b/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkViewQuery.graphql.ts index 178230c8e..b9d022100 100644 --- a/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkViewQuery.graphql.ts +++ b/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkViewQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<1abc57788d51404af0931c7e120c01a7>> + * @generated SignedSource<<0185ea6dc887f2bbb5ca70ec28e916b2>> * @lightSyntaxTransform * @nogrep */ @@ -9,27 +9,23 @@ // @ts-nocheck import { ConcreteRequest } from 'relay-runtime'; -export type MitigationImportance = "ADVANCED" | "MANDATORY" | "PREFERRED"; -export type MitigationState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | "NOT_STARTED"; export type FrameworkViewQuery$variables = { frameworkId: string; }; export type FrameworkViewQuery$data = { readonly node: { - readonly description?: string; - readonly id: string; - readonly mitigations?: { + readonly controls?: { readonly edges: ReadonlyArray<{ readonly node: { - readonly category: string; readonly description: string; readonly id: string; - readonly importance: MitigationImportance; readonly name: string; - readonly state: MitigationState; + readonly referenceId: string; }; }>; }; + readonly description?: string; + readonly id: string; readonly name?: string; }; }; @@ -75,17 +71,25 @@ v4 = { "storageKey": null }, v5 = { + "kind": "Literal", + "name": "orderBy", + "value": { + "direction": "ASC", + "field": "CREATED_AT" + } +}, +v6 = { "alias": null, "args": null, "kind": "ScalarField", "name": "__typename", "storageKey": null }, -v6 = [ +v7 = [ { "alias": null, "args": null, - "concreteType": "MitigationEdge", + "concreteType": "ControlEdge", "kind": "LinkedField", "name": "edges", "plural": true, @@ -93,36 +97,22 @@ v6 = [ { "alias": null, "args": null, - "concreteType": "Mitigation", + "concreteType": "Control", "kind": "LinkedField", "name": "node", "plural": false, "selections": [ (v2/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "referenceId", + "storageKey": null + }, (v3/*: any*/), (v4/*: any*/), - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "state", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "category", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "importance", - "storageKey": null - }, - (v5/*: any*/) + (v6/*: any*/) ], "storageKey": null }, @@ -162,12 +152,13 @@ v6 = [ "storageKey": null } ], -v7 = [ +v8 = [ { "kind": "Literal", "name": "first", "value": 100 - } + }, + (v5/*: any*/) ]; return { "fragment": { @@ -191,14 +182,16 @@ return { (v3/*: any*/), (v4/*: any*/), { - "alias": "mitigations", - "args": null, - "concreteType": "MitigationConnection", + "alias": "controls", + "args": [ + (v5/*: any*/) + ], + "concreteType": "ControlConnection", "kind": "LinkedField", - "name": "__FrameworkView_mitigations_connection", + "name": "__FrameworkView_controls_connection", "plural": false, - "selections": (v6/*: any*/), - "storageKey": null + "selections": (v7/*: any*/), + "storageKey": "__FrameworkView_controls_connection(orderBy:{\"direction\":\"ASC\",\"field\":\"CREATED_AT\"})" } ], "type": "Framework", @@ -225,7 +218,7 @@ return { "name": "node", "plural": false, "selections": [ - (v5/*: any*/), + (v6/*: any*/), (v2/*: any*/), { "kind": "InlineFragment", @@ -234,22 +227,24 @@ return { (v4/*: any*/), { "alias": null, - "args": (v7/*: any*/), - "concreteType": "MitigationConnection", + "args": (v8/*: any*/), + "concreteType": "ControlConnection", "kind": "LinkedField", - "name": "mitigations", + "name": "controls", "plural": false, - "selections": (v6/*: any*/), - "storageKey": "mitigations(first:100)" + "selections": (v7/*: any*/), + "storageKey": "controls(first:100,orderBy:{\"direction\":\"ASC\",\"field\":\"CREATED_AT\"})" }, { "alias": null, - "args": (v7/*: any*/), - "filters": null, + "args": (v8/*: any*/), + "filters": [ + "orderBy" + ], "handle": "connection", - "key": "FrameworkView_mitigations", + "key": "FrameworkView_controls", "kind": "LinkedHandle", - "name": "mitigations" + "name": "controls" } ], "type": "Framework", @@ -261,7 +256,7 @@ return { ] }, "params": { - "cacheID": "c60a352d14662dd43ab93b9a4dcebee8", + "cacheID": "a1fa5a0efc291c92478c061d32b90e43", "id": null, "metadata": { "connection": [ @@ -271,18 +266,18 @@ return { "direction": "forward", "path": [ "node", - "mitigations" + "controls" ] } ] }, "name": "FrameworkViewQuery", "operationKind": "query", - "text": "query FrameworkViewQuery(\n $frameworkId: ID!\n) {\n node(id: $frameworkId) {\n __typename\n id\n ... on Framework {\n name\n description\n mitigations(first: 100) {\n edges {\n node {\n id\n name\n description\n state\n category\n importance\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n" + "text": "query FrameworkViewQuery(\n $frameworkId: ID!\n) {\n node(id: $frameworkId) {\n __typename\n id\n ... on Framework {\n name\n description\n controls(first: 100, orderBy: {field: CREATED_AT, direction: ASC}) {\n edges {\n node {\n id\n referenceId\n name\n description\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n" } }; })(); -(node as any).hash = "f65af17f461e1c112573c0d14d08479d"; +(node as any).hash = "ff9593fc321c840ae9ef1da48a13c3e5"; export default node; diff --git a/apps/console/src/pages/organizations/frameworks/__generated__/UpdateFrameworkViewQuery.graphql.ts b/apps/console/src/pages/organizations/frameworks/__generated__/UpdateFrameworkViewQuery.graphql.ts index a77cab10e..4e8d90dea 100644 --- a/apps/console/src/pages/organizations/frameworks/__generated__/UpdateFrameworkViewQuery.graphql.ts +++ b/apps/console/src/pages/organizations/frameworks/__generated__/UpdateFrameworkViewQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<08d71ea16ac38e11d5b1ce3fb11bcaf3>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -17,7 +17,6 @@ export type UpdateFrameworkViewQuery$data = { readonly description?: string; readonly id?: string; readonly name?: string; - readonly version?: number; }; }; export type UpdateFrameworkViewQuery = { @@ -60,13 +59,6 @@ v4 = { "kind": "ScalarField", "name": "description", "storageKey": null -}, -v5 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "version", - "storageKey": null }; return { "fragment": { @@ -88,8 +80,7 @@ return { "selections": [ (v2/*: any*/), (v3/*: any*/), - (v4/*: any*/), - (v5/*: any*/) + (v4/*: any*/) ], "type": "Framework", "abstractKey": null @@ -127,8 +118,7 @@ return { "kind": "InlineFragment", "selections": [ (v3/*: any*/), - (v4/*: any*/), - (v5/*: any*/) + (v4/*: any*/) ], "type": "Framework", "abstractKey": null @@ -139,16 +129,16 @@ return { ] }, "params": { - "cacheID": "180cbef2756c525af017ca6996a7c2d6", + "cacheID": "5c423e61373989c4576989cb6c732547", "id": null, "metadata": {}, "name": "UpdateFrameworkViewQuery", "operationKind": "query", - "text": "query UpdateFrameworkViewQuery(\n $frameworkId: ID!\n) {\n node(id: $frameworkId) {\n __typename\n ... on Framework {\n id\n name\n description\n version\n }\n id\n }\n}\n" + "text": "query UpdateFrameworkViewQuery(\n $frameworkId: ID!\n) {\n node(id: $frameworkId) {\n __typename\n ... on Framework {\n id\n name\n description\n }\n id\n }\n}\n" } }; })(); -(node as any).hash = "d644d01f1d8a31b6c87c7f6360996796"; +(node as any).hash = "416b7f0dbf33cbfe39578fc3243bf423"; export default node; diff --git a/apps/console/src/pages/organizations/frameworks/__generated__/UpdateFrameworkViewUpdateFrameworkMutation.graphql.ts b/apps/console/src/pages/organizations/frameworks/__generated__/UpdateFrameworkViewUpdateFrameworkMutation.graphql.ts index 8e1bf968f..e66b4320f 100644 --- a/apps/console/src/pages/organizations/frameworks/__generated__/UpdateFrameworkViewUpdateFrameworkMutation.graphql.ts +++ b/apps/console/src/pages/organizations/frameworks/__generated__/UpdateFrameworkViewUpdateFrameworkMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<115402b83869511df5e91ab206ae99e3>> + * @generated SignedSource<<9c98acb5c320005e303cd815550c0d74>> * @lightSyntaxTransform * @nogrep */ @@ -11,7 +11,6 @@ import { ConcreteRequest } from 'relay-runtime'; export type UpdateFrameworkInput = { description?: string | null | undefined; - expectedVersion: number; id: string; name?: string | null | undefined; }; @@ -24,7 +23,6 @@ export type UpdateFrameworkViewUpdateFrameworkMutation$data = { readonly description: string; readonly id: string; readonly name: string; - readonly version: number; }; }; }; @@ -84,13 +82,6 @@ v1 = [ "kind": "ScalarField", "name": "description", "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "version", - "storageKey": null } ], "storageKey": null @@ -117,16 +108,16 @@ return { "selections": (v1/*: any*/) }, "params": { - "cacheID": "08fc83afdc1227aa23cac0778cdd2cc0", + "cacheID": "8a41e224572c2419fc49c7fef6908d63", "id": null, "metadata": {}, "name": "UpdateFrameworkViewUpdateFrameworkMutation", "operationKind": "mutation", - "text": "mutation UpdateFrameworkViewUpdateFrameworkMutation(\n $input: UpdateFrameworkInput!\n) {\n updateFramework(input: $input) {\n framework {\n id\n name\n description\n version\n }\n }\n}\n" + "text": "mutation UpdateFrameworkViewUpdateFrameworkMutation(\n $input: UpdateFrameworkInput!\n) {\n updateFramework(input: $input) {\n framework {\n id\n name\n description\n }\n }\n}\n" } }; })(); -(node as any).hash = "6dfde24d73c2cbc0c5ca147d076fbd26"; +(node as any).hash = "9b201a966e844a77d3d920a7e0165d15"; export default node; diff --git a/apps/console/src/pages/organizations/frameworks/mitigations/MitigationView.tsx b/apps/console/src/pages/organizations/frameworks/mitigations/MitigationView.tsx index c9d06ffd8..9e647c6f8 100644 --- a/apps/console/src/pages/organizations/frameworks/mitigations/MitigationView.tsx +++ b/apps/console/src/pages/organizations/frameworks/mitigations/MitigationView.tsx @@ -131,7 +131,6 @@ const mitigationViewQuery = graphql` state importance category - version tasks(first: 100) @connection(key: "MitigationView_tasks") { __id edges { @@ -141,7 +140,6 @@ const mitigationViewQuery = graphql` description state timeEstimate - version assignedTo { id fullName @@ -178,7 +176,6 @@ const updateTaskStateMutation = graphql` id state timeEstimate - version } } } @@ -197,7 +194,6 @@ const createTaskMutation = graphql` description timeEstimate state - version assignedTo { id fullName @@ -271,7 +267,6 @@ const assignTaskMutation = graphql` assignTask(input: $input) { task { id - version assignedTo { id fullName @@ -287,7 +282,6 @@ const unassignTaskMutation = graphql` unassignTask(input: $input) { task { id - version assignedTo { id fullName @@ -306,7 +300,6 @@ const updateMitigationStateMutation = graphql` mitigation { id state - version } } } @@ -627,11 +620,7 @@ function MitigationViewContent({ setSearchParams(searchParams); }; - const handleToggleTaskState = ( - taskId: string, - currentState: string, - version: number - ) => { + const handleToggleTaskState = (taskId: string, currentState: string) => { const newState = currentState === "DONE" ? "TODO" : "DONE"; updateTask({ @@ -639,7 +628,6 @@ function MitigationViewContent({ input: { taskId, state: newState, - expectedVersion: version, }, }, onCompleted: () => { @@ -647,7 +635,6 @@ function MitigationViewContent({ setSelectedTask({ ...selectedTask, state: newState, - version: version + 1, }); } }, @@ -1114,7 +1101,6 @@ function MitigationViewContent({ | "IN_PROGRESS" | "IMPLEMENTED" | "NOT_APPLICABLE", - expectedVersion: data.mitigation.version!, }, }, onCompleted: () => { @@ -1180,7 +1166,7 @@ function MitigationViewContent({ // Function to handle saving the updated duration const handleSaveDuration = useCallback( - (taskId: string, version: number) => { + (taskId: string) => { // Convert to ISO duration format let duration = "P"; @@ -1211,7 +1197,6 @@ function MitigationViewContent({ input: { taskId, timeEstimate, - expectedVersion: version, }, }, onCompleted: () => { @@ -1222,7 +1207,6 @@ function MitigationViewContent({ setSelectedTask({ ...selectedTask, timeEstimate, - version: version + 1, }); } }, @@ -1495,7 +1479,7 @@ function MitigationViewContent({ onClick={(e) => { e.stopPropagation(); // Prevent task selection when checkbox is clicked if (task?.id && task?.state) { - handleToggleTaskState(task.id, task.state, task.version); + handleToggleTaskState(task.id, task.state); } }} > @@ -1963,12 +1947,7 @@ function MitigationViewContent({
@@ -2256,8 +2235,7 @@ function MitigationViewContent({ onClick={() => handleToggleTaskState( selectedTask.id, - selectedTask.state || "TODO", - selectedTask.version + selectedTask.state || "TODO" ) } > @@ -2271,8 +2249,7 @@ function MitigationViewContent({ onClick={() => handleToggleTaskState( selectedTask.id, - selectedTask.state || "DONE", - selectedTask.version + selectedTask.state || "DONE" ) } > diff --git a/apps/console/src/pages/organizations/frameworks/mitigations/UpdateMitigationView.tsx b/apps/console/src/pages/organizations/frameworks/mitigations/UpdateMitigationView.tsx index 36cded475..f7e8b7bcb 100644 --- a/apps/console/src/pages/organizations/frameworks/mitigations/UpdateMitigationView.tsx +++ b/apps/console/src/pages/organizations/frameworks/mitigations/UpdateMitigationView.tsx @@ -42,7 +42,6 @@ const updateMitigationMutation = graphql` category importance state - version } } } @@ -58,7 +57,6 @@ const updateMitigationQuery = graphql` category importance state - version } } } @@ -191,7 +189,6 @@ function UpdateMitigationViewContent({ const input: { id: string; - expectedVersion: number; name?: string; description?: string; category?: string; @@ -199,7 +196,6 @@ function UpdateMitigationViewContent({ importance?: MitigationImportance; } = { id: mitigationId!, - expectedVersion: data.node.version, }; if (editedFields.has("name")) { diff --git a/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/CreateMitigationViewCreateMitigationMutation.graphql.ts b/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/CreateMitigationViewCreateMitigationMutation.graphql.ts index 63c02a2c3..4f0b89553 100644 --- a/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/CreateMitigationViewCreateMitigationMutation.graphql.ts +++ b/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/CreateMitigationViewCreateMitigationMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<22a6faa89009d6784bc9a89c2456f12e>> + * @generated SignedSource<<07454812ea72dd51d0b615737e75a2a1>> * @lightSyntaxTransform * @nogrep */ @@ -14,9 +14,9 @@ export type MitigationState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | export type CreateMitigationInput = { category: string; description: string; - frameworkId: string; importance: MitigationImportance; name: string; + organizationId: string; }; export type CreateMitigationViewCreateMitigationMutation$variables = { connections: ReadonlyArray; diff --git a/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/MitigationViewAssignTaskMutation.graphql.ts b/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/MitigationViewAssignTaskMutation.graphql.ts index ee7a65050..2b7a5632f 100644 --- a/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/MitigationViewAssignTaskMutation.graphql.ts +++ b/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/MitigationViewAssignTaskMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -25,7 +25,6 @@ export type MitigationViewAssignTaskMutation$data = { readonly primaryEmailAddress: string; } | null | undefined; readonly id: string; - readonly version: number; }; }; }; @@ -73,13 +72,6 @@ v2 = [ "plural": false, "selections": [ (v1/*: any*/), - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "version", - "storageKey": null - }, { "alias": null, "args": null, @@ -131,16 +123,16 @@ return { "selections": (v2/*: any*/) }, "params": { - "cacheID": "b336b45b0dad20968af93b52cd0aac48", + "cacheID": "c01a2f758aea8268b73365c426b7c08a", "id": null, "metadata": {}, "name": "MitigationViewAssignTaskMutation", "operationKind": "mutation", - "text": "mutation MitigationViewAssignTaskMutation(\n $input: AssignTaskInput!\n) {\n assignTask(input: $input) {\n task {\n id\n version\n assignedTo {\n id\n fullName\n primaryEmailAddress\n }\n }\n }\n}\n" + "text": "mutation MitigationViewAssignTaskMutation(\n $input: AssignTaskInput!\n) {\n assignTask(input: $input) {\n task {\n id\n assignedTo {\n id\n fullName\n primaryEmailAddress\n }\n }\n }\n}\n" } }; })(); -(node as any).hash = "6446830361465b47f185c50b2886a422"; +(node as any).hash = "2b01502cb830a0d914553e01ceb2445f"; export default node; diff --git a/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/MitigationViewCreateTaskMutation.graphql.ts b/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/MitigationViewCreateTaskMutation.graphql.ts index 83b7d8446..5716b5ffd 100644 --- a/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/MitigationViewCreateTaskMutation.graphql.ts +++ b/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/MitigationViewCreateTaskMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<958545e211155678a1a43d8ef1717071>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -35,7 +35,6 @@ export type MitigationViewCreateTaskMutation$data = { readonly name: string; readonly state: TaskState; readonly timeEstimate: any | null | undefined; - readonly version: number; }; }; }; @@ -115,13 +114,6 @@ v4 = { "name": "state", "storageKey": null }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "version", - "storageKey": null - }, { "alias": null, "args": null, @@ -220,16 +212,16 @@ return { ] }, "params": { - "cacheID": "dedf546da903060c79209ab7695bde12", + "cacheID": "273c3d2ae0de44280e85e47db23db892", "id": null, "metadata": {}, "name": "MitigationViewCreateTaskMutation", "operationKind": "mutation", - "text": "mutation MitigationViewCreateTaskMutation(\n $input: CreateTaskInput!\n) {\n createTask(input: $input) {\n taskEdge {\n node {\n id\n name\n description\n timeEstimate\n state\n version\n assignedTo {\n id\n fullName\n primaryEmailAddress\n }\n }\n }\n }\n}\n" + "text": "mutation MitigationViewCreateTaskMutation(\n $input: CreateTaskInput!\n) {\n createTask(input: $input) {\n taskEdge {\n node {\n id\n name\n description\n timeEstimate\n state\n assignedTo {\n id\n fullName\n primaryEmailAddress\n }\n }\n }\n }\n}\n" } }; })(); -(node as any).hash = "683fe4d2a3d33e989a8b5593f99799a5"; +(node as any).hash = "ad8a56e4976e9dc15f4ee8e1b32d9fa9"; export default node; diff --git a/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/MitigationViewQuery.graphql.ts b/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/MitigationViewQuery.graphql.ts index ac0269c81..6c698da79 100644 --- a/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/MitigationViewQuery.graphql.ts +++ b/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/MitigationViewQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<<5baf9229b2f520bcfa43960bd6af05e0>> * @lightSyntaxTransform * @nogrep */ @@ -54,11 +54,9 @@ export type MitigationViewQuery$data = { readonly name: string; readonly state: TaskState; readonly timeEstimate: any | null | undefined; - readonly version: number; }; }>; }; - readonly version?: number; }; }; export type MitigationViewQuery = { @@ -124,20 +122,13 @@ v7 = { "storageKey": null }, v8 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "version", - "storageKey": null -}, -v9 = { "alias": null, "args": null, "kind": "ScalarField", "name": "timeEstimate", "storageKey": null }, -v10 = { +v9 = { "alias": null, "args": null, "concreteType": "People", @@ -163,21 +154,21 @@ v10 = { ], "storageKey": null }, -v11 = { +v10 = { "alias": null, "args": null, "kind": "ScalarField", "name": "__typename", "storageKey": null }, -v12 = { +v11 = { "alias": null, "args": null, "kind": "ScalarField", "name": "cursor", "storageKey": null }, -v13 = { +v12 = { "alias": null, "args": null, "concreteType": "PageInfo", @@ -202,7 +193,7 @@ v13 = { ], "storageKey": null }, -v14 = { +v13 = { "kind": "ClientExtension", "selections": [ { @@ -214,7 +205,7 @@ v14 = { } ] }, -v15 = [ +v14 = [ { "alias": null, "args": null, @@ -275,25 +266,25 @@ v15 = [ "name": "createdAt", "storageKey": null }, - (v11/*: any*/) + (v10/*: any*/) ], "storageKey": null }, - (v12/*: any*/) + (v11/*: any*/) ], "storageKey": null }, - (v13/*: any*/), - (v14/*: any*/) + (v12/*: any*/), + (v13/*: any*/) ], -v16 = [ +v15 = [ { "kind": "Literal", "name": "first", "value": 100 } ], -v17 = [ +v16 = [ { "kind": "Literal", "name": "first", @@ -324,7 +315,6 @@ return { (v5/*: any*/), (v6/*: any*/), (v7/*: any*/), - (v8/*: any*/), { "alias": "tasks", "args": null, @@ -353,9 +343,8 @@ return { (v3/*: any*/), (v4/*: any*/), (v5/*: any*/), - (v9/*: any*/), (v8/*: any*/), - (v10/*: any*/), + (v9/*: any*/), { "alias": "evidences", "args": null, @@ -363,19 +352,19 @@ return { "kind": "LinkedField", "name": "__MitigationView_evidences_connection", "plural": false, - "selections": (v15/*: any*/), + "selections": (v14/*: any*/), "storageKey": null }, - (v11/*: any*/) + (v10/*: any*/) ], "storageKey": null }, - (v12/*: any*/) + (v11/*: any*/) ], "storageKey": null }, - (v13/*: any*/), - (v14/*: any*/) + (v12/*: any*/), + (v13/*: any*/) ], "storageKey": null } @@ -404,7 +393,7 @@ return { "name": "node", "plural": false, "selections": [ - (v11/*: any*/), + (v10/*: any*/), (v2/*: any*/), { "kind": "InlineFragment", @@ -414,10 +403,9 @@ return { (v5/*: any*/), (v6/*: any*/), (v7/*: any*/), - (v8/*: any*/), { "alias": null, - "args": (v16/*: any*/), + "args": (v15/*: any*/), "concreteType": "TaskConnection", "kind": "LinkedField", "name": "tasks", @@ -443,44 +431,43 @@ return { (v3/*: any*/), (v4/*: any*/), (v5/*: any*/), - (v9/*: any*/), (v8/*: any*/), - (v10/*: any*/), + (v9/*: any*/), { "alias": null, - "args": (v17/*: any*/), + "args": (v16/*: any*/), "concreteType": "EvidenceConnection", "kind": "LinkedField", "name": "evidences", "plural": false, - "selections": (v15/*: any*/), + "selections": (v14/*: any*/), "storageKey": "evidences(first:50)" }, { "alias": null, - "args": (v17/*: any*/), + "args": (v16/*: any*/), "filters": null, "handle": "connection", "key": "MitigationView_evidences", "kind": "LinkedHandle", "name": "evidences" }, - (v11/*: any*/) + (v10/*: any*/) ], "storageKey": null }, - (v12/*: any*/) + (v11/*: any*/) ], "storageKey": null }, - (v13/*: any*/), - (v14/*: any*/) + (v12/*: any*/), + (v13/*: any*/) ], "storageKey": "tasks(first:100)" }, { "alias": null, - "args": (v16/*: any*/), + "args": (v15/*: any*/), "filters": null, "handle": "connection", "key": "MitigationView_tasks", @@ -497,7 +484,7 @@ return { ] }, "params": { - "cacheID": "8e9c3cef43daff0bdbfff4e33d7c70ad", + "cacheID": "27e466aa3f7c929738fc108627fd5aa8", "id": null, "metadata": { "connection": [ @@ -520,11 +507,11 @@ return { }, "name": "MitigationViewQuery", "operationKind": "query", - "text": "query MitigationViewQuery(\n $mitigationId: ID!\n) {\n mitigation: node(id: $mitigationId) {\n __typename\n id\n ... on Mitigation {\n name\n description\n state\n importance\n category\n version\n tasks(first: 100) {\n edges {\n node {\n id\n name\n description\n state\n timeEstimate\n version\n assignedTo {\n id\n fullName\n primaryEmailAddress\n }\n evidences(first: 50) {\n edges {\n node {\n id\n mimeType\n filename\n size\n state\n type\n url\n createdAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n" + "text": "query MitigationViewQuery(\n $mitigationId: ID!\n) {\n mitigation: node(id: $mitigationId) {\n __typename\n id\n ... on Mitigation {\n name\n description\n state\n importance\n category\n tasks(first: 100) {\n edges {\n node {\n id\n name\n description\n state\n timeEstimate\n assignedTo {\n id\n fullName\n primaryEmailAddress\n }\n evidences(first: 50) {\n edges {\n node {\n id\n mimeType\n filename\n size\n state\n type\n url\n createdAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n" } }; })(); -(node as any).hash = "f0baeab367be46290f2f15df836ed153"; +(node as any).hash = "c26df14ee0f01a46868fdd73baa76638"; export default node; diff --git a/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/MitigationViewUnassignTaskMutation.graphql.ts b/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/MitigationViewUnassignTaskMutation.graphql.ts index ff8ff8ece..d66c4167f 100644 --- a/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/MitigationViewUnassignTaskMutation.graphql.ts +++ b/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/MitigationViewUnassignTaskMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<9cb5a3f1c6f0c2a3265ecdc6ececd13d>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -24,7 +24,6 @@ export type MitigationViewUnassignTaskMutation$data = { readonly primaryEmailAddress: string; } | null | undefined; readonly id: string; - readonly version: number; }; }; }; @@ -72,13 +71,6 @@ v2 = [ "plural": false, "selections": [ (v1/*: any*/), - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "version", - "storageKey": null - }, { "alias": null, "args": null, @@ -130,16 +122,16 @@ return { "selections": (v2/*: any*/) }, "params": { - "cacheID": "e493ae65ba1e53627dd3f260e8814e41", + "cacheID": "6cf554a835936763f3b4f55f04c9771f", "id": null, "metadata": {}, "name": "MitigationViewUnassignTaskMutation", "operationKind": "mutation", - "text": "mutation MitigationViewUnassignTaskMutation(\n $input: UnassignTaskInput!\n) {\n unassignTask(input: $input) {\n task {\n id\n version\n assignedTo {\n id\n fullName\n primaryEmailAddress\n }\n }\n }\n}\n" + "text": "mutation MitigationViewUnassignTaskMutation(\n $input: UnassignTaskInput!\n) {\n unassignTask(input: $input) {\n task {\n id\n assignedTo {\n id\n fullName\n primaryEmailAddress\n }\n }\n }\n}\n" } }; })(); -(node as any).hash = "3f5a0d2b753d0a759e3004c0b557c505"; +(node as any).hash = "07d79596fef3416fdc001077d2d51c75"; export default node; diff --git a/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/MitigationViewUpdateMitigationStateMutation.graphql.ts b/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/MitigationViewUpdateMitigationStateMutation.graphql.ts index 3ed2dbae8..fe2e6342b 100644 --- a/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/MitigationViewUpdateMitigationStateMutation.graphql.ts +++ b/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/MitigationViewUpdateMitigationStateMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<3aa56a1042b1f9492cfc5bd5d5f55d9d>> + * @generated SignedSource<<1d924bb487fd93636fa7207c5aa08cdc>> * @lightSyntaxTransform * @nogrep */ @@ -14,7 +14,6 @@ export type MitigationState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | export type UpdateMitigationInput = { category?: string | null | undefined; description?: string | null | undefined; - expectedVersion: number; id: string; importance?: MitigationImportance | null | undefined; name?: string | null | undefined; @@ -28,7 +27,6 @@ export type MitigationViewUpdateMitigationStateMutation$data = { readonly mitigation: { readonly id: string; readonly state: MitigationState; - readonly version: number; }; }; }; @@ -81,13 +79,6 @@ v1 = [ "kind": "ScalarField", "name": "state", "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "version", - "storageKey": null } ], "storageKey": null @@ -114,16 +105,16 @@ return { "selections": (v1/*: any*/) }, "params": { - "cacheID": "4a85c6e15d6ff91ccaa7976f9b0a0c6f", + "cacheID": "7e4a92a089fa5927b0239004c3fb5f47", "id": null, "metadata": {}, "name": "MitigationViewUpdateMitigationStateMutation", "operationKind": "mutation", - "text": "mutation MitigationViewUpdateMitigationStateMutation(\n $input: UpdateMitigationInput!\n) {\n updateMitigation(input: $input) {\n mitigation {\n id\n state\n version\n }\n }\n}\n" + "text": "mutation MitigationViewUpdateMitigationStateMutation(\n $input: UpdateMitigationInput!\n) {\n updateMitigation(input: $input) {\n mitigation {\n id\n state\n }\n }\n}\n" } }; })(); -(node as any).hash = "2eb2c1f791262f502df22ca0c16682a7"; +(node as any).hash = "e985902d55a537a08b8a2d4482abe0b3"; export default node; diff --git a/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/MitigationViewUpdateTaskStateMutation.graphql.ts b/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/MitigationViewUpdateTaskStateMutation.graphql.ts index 81127d344..ebee2c524 100644 --- a/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/MitigationViewUpdateTaskStateMutation.graphql.ts +++ b/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/MitigationViewUpdateTaskStateMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<4aed09b28d5d1ec083abfeeb874b9ce3>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -12,7 +12,6 @@ import { ConcreteRequest } from 'relay-runtime'; export type TaskState = "DONE" | "TODO"; export type UpdateTaskInput = { description?: string | null | undefined; - expectedVersion: number; name?: string | null | undefined; state?: TaskState | null | undefined; taskId: string; @@ -27,7 +26,6 @@ export type MitigationViewUpdateTaskStateMutation$data = { readonly id: string; readonly state: TaskState; readonly timeEstimate: any | null | undefined; - readonly version: number; }; }; }; @@ -87,13 +85,6 @@ v1 = [ "kind": "ScalarField", "name": "timeEstimate", "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "version", - "storageKey": null } ], "storageKey": null @@ -120,16 +111,16 @@ return { "selections": (v1/*: any*/) }, "params": { - "cacheID": "8de0d061237d4ebcbb366b8eefff6d94", + "cacheID": "f8d2ca86f8e7856b3d24bcd810490a6a", "id": null, "metadata": {}, "name": "MitigationViewUpdateTaskStateMutation", "operationKind": "mutation", - "text": "mutation MitigationViewUpdateTaskStateMutation(\n $input: UpdateTaskInput!\n) {\n updateTask(input: $input) {\n task {\n id\n state\n timeEstimate\n version\n }\n }\n}\n" + "text": "mutation MitigationViewUpdateTaskStateMutation(\n $input: UpdateTaskInput!\n) {\n updateTask(input: $input) {\n task {\n id\n state\n timeEstimate\n }\n }\n}\n" } }; })(); -(node as any).hash = "81dca6c475859526370ef60b4757428b"; +(node as any).hash = "7ab07c94be8f56a93d39178828d85823"; export default node; diff --git a/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/UpdateMitigationViewQuery.graphql.ts b/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/UpdateMitigationViewQuery.graphql.ts index a4bf0899d..6f53673c2 100644 --- a/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/UpdateMitigationViewQuery.graphql.ts +++ b/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/UpdateMitigationViewQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<338c4c7afa073c41eacac21a2996d1f9>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -22,7 +22,6 @@ export type UpdateMitigationViewQuery$data = { readonly importance?: MitigationImportance; readonly name?: string; readonly state?: MitigationState; - readonly version?: number; }; }; export type UpdateMitigationViewQuery = { @@ -86,13 +85,6 @@ v7 = { "kind": "ScalarField", "name": "state", "storageKey": null -}, -v8 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "version", - "storageKey": null }; return { "fragment": { @@ -117,8 +109,7 @@ return { (v4/*: any*/), (v5/*: any*/), (v6/*: any*/), - (v7/*: any*/), - (v8/*: any*/) + (v7/*: any*/) ], "type": "Mitigation", "abstractKey": null @@ -159,8 +150,7 @@ return { (v4/*: any*/), (v5/*: any*/), (v6/*: any*/), - (v7/*: any*/), - (v8/*: any*/) + (v7/*: any*/) ], "type": "Mitigation", "abstractKey": null @@ -171,16 +161,16 @@ return { ] }, "params": { - "cacheID": "c2e9133d77f3e090267b19f4ab270c72", + "cacheID": "81b47701802e110078ee303a83c19d88", "id": null, "metadata": {}, "name": "UpdateMitigationViewQuery", "operationKind": "query", - "text": "query UpdateMitigationViewQuery(\n $mitigationId: ID!\n) {\n node(id: $mitigationId) {\n __typename\n ... on Mitigation {\n id\n name\n description\n category\n importance\n state\n version\n }\n id\n }\n}\n" + "text": "query UpdateMitigationViewQuery(\n $mitigationId: ID!\n) {\n node(id: $mitigationId) {\n __typename\n ... on Mitigation {\n id\n name\n description\n category\n importance\n state\n }\n id\n }\n}\n" } }; })(); -(node as any).hash = "3e977744beb9c403aa8be232c8112a9b"; +(node as any).hash = "126fe89ef3da933cfb7847ff05179832"; export default node; diff --git a/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/UpdateMitigationViewUpdateMitigationMutation.graphql.ts b/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/UpdateMitigationViewUpdateMitigationMutation.graphql.ts index 4f38e0846..df1610788 100644 --- a/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/UpdateMitigationViewUpdateMitigationMutation.graphql.ts +++ b/apps/console/src/pages/organizations/frameworks/mitigations/__generated__/UpdateMitigationViewUpdateMitigationMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<39cfe3277c49d7a0ff2099d6ad579e7d>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -14,7 +14,6 @@ export type MitigationState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | export type UpdateMitigationInput = { category?: string | null | undefined; description?: string | null | undefined; - expectedVersion: number; id: string; importance?: MitigationImportance | null | undefined; name?: string | null | undefined; @@ -32,7 +31,6 @@ export type UpdateMitigationViewUpdateMitigationMutation$data = { readonly importance: MitigationImportance; readonly name: string; readonly state: MitigationState; - readonly version: number; }; }; }; @@ -113,13 +111,6 @@ v1 = [ "kind": "ScalarField", "name": "state", "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "version", - "storageKey": null } ], "storageKey": null @@ -146,16 +137,16 @@ return { "selections": (v1/*: any*/) }, "params": { - "cacheID": "5ebe3d38712b444c98c08661b7431da3", + "cacheID": "4a58a30780331c2d00bab2c3b613635b", "id": null, "metadata": {}, "name": "UpdateMitigationViewUpdateMitigationMutation", "operationKind": "mutation", - "text": "mutation UpdateMitigationViewUpdateMitigationMutation(\n $input: UpdateMitigationInput!\n) {\n updateMitigation(input: $input) {\n mitigation {\n id\n name\n description\n category\n importance\n state\n version\n }\n }\n}\n" + "text": "mutation UpdateMitigationViewUpdateMitigationMutation(\n $input: UpdateMitigationInput!\n) {\n updateMitigation(input: $input) {\n mitigation {\n id\n name\n description\n category\n importance\n state\n }\n }\n}\n" } }; })(); -(node as any).hash = "c68d76d527359d182749b309d2d8d6dd"; +(node as any).hash = "b8cf81c9342fe42160ee39f572b01b24"; export default node; diff --git a/apps/console/src/pages/organizations/people/PeopleView.tsx b/apps/console/src/pages/organizations/people/PeopleView.tsx index b5003356d..99355d0fc 100644 --- a/apps/console/src/pages/organizations/people/PeopleView.tsx +++ b/apps/console/src/pages/organizations/people/PeopleView.tsx @@ -37,7 +37,6 @@ const peopleViewQuery = graphql` kind createdAt updatedAt - version } } } @@ -53,7 +52,6 @@ const updatePeopleMutation = graphql` additionalEmailAddresses kind updatedAt - version } } } @@ -114,7 +112,6 @@ function PeopleViewContent({ variables: { input: { id: data.node.id, - expectedVersion: data.node.version, ...formData, }, }, @@ -144,7 +141,7 @@ function PeopleViewContent({ } }, }); - }, [commit, data.node.id, data.node.version, formData, loadQuery, toast]); + }, [commit, data.node.id, formData, loadQuery, toast]); const handleFieldChange = (field: keyof typeof formData, value: unknown) => { setFormData((prev) => ({ diff --git a/apps/console/src/pages/organizations/people/__generated__/PeopleViewQuery.graphql.ts b/apps/console/src/pages/organizations/people/__generated__/PeopleViewQuery.graphql.ts index 566f6759f..cbc06a3de 100644 --- a/apps/console/src/pages/organizations/people/__generated__/PeopleViewQuery.graphql.ts +++ b/apps/console/src/pages/organizations/people/__generated__/PeopleViewQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<207a779cf8cbf3e42191be9825d4b653>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -22,7 +22,6 @@ export type PeopleViewQuery$data = { readonly kind?: PeopleKind; readonly primaryEmailAddress?: string; readonly updatedAt?: string; - readonly version?: number; }; }; export type PeopleViewQuery = { @@ -93,13 +92,6 @@ v8 = { "kind": "ScalarField", "name": "updatedAt", "storageKey": null -}, -v9 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "version", - "storageKey": null }; return { "fragment": { @@ -125,8 +117,7 @@ return { (v5/*: any*/), (v6/*: any*/), (v7/*: any*/), - (v8/*: any*/), - (v9/*: any*/) + (v8/*: any*/) ], "type": "People", "abstractKey": null @@ -168,8 +159,7 @@ return { (v5/*: any*/), (v6/*: any*/), (v7/*: any*/), - (v8/*: any*/), - (v9/*: any*/) + (v8/*: any*/) ], "type": "People", "abstractKey": null @@ -180,16 +170,16 @@ return { ] }, "params": { - "cacheID": "c392876240212ba16428ae5edb843d47", + "cacheID": "fba00a5764659b366e4e11829d75854d", "id": null, "metadata": {}, "name": "PeopleViewQuery", "operationKind": "query", - "text": "query PeopleViewQuery(\n $peopleId: ID!\n) {\n node(id: $peopleId) {\n __typename\n ... on People {\n id\n fullName\n primaryEmailAddress\n additionalEmailAddresses\n kind\n createdAt\n updatedAt\n version\n }\n id\n }\n}\n" + "text": "query PeopleViewQuery(\n $peopleId: ID!\n) {\n node(id: $peopleId) {\n __typename\n ... on People {\n id\n fullName\n primaryEmailAddress\n additionalEmailAddresses\n kind\n createdAt\n updatedAt\n }\n id\n }\n}\n" } }; })(); -(node as any).hash = "4fc97d6cd7fc4590b7be7b5a79ae7ab0"; +(node as any).hash = "b7652de1ad8de6028f493255221f6ce5"; export default node; diff --git a/apps/console/src/pages/organizations/people/__generated__/PeopleViewUpdatePeopleMutation.graphql.ts b/apps/console/src/pages/organizations/people/__generated__/PeopleViewUpdatePeopleMutation.graphql.ts index 293d83efe..74045d95e 100644 --- a/apps/console/src/pages/organizations/people/__generated__/PeopleViewUpdatePeopleMutation.graphql.ts +++ b/apps/console/src/pages/organizations/people/__generated__/PeopleViewUpdatePeopleMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<<552b78c5e34c5ce2065dd1190f47f632>> * @lightSyntaxTransform * @nogrep */ @@ -12,7 +12,6 @@ import { ConcreteRequest } from 'relay-runtime'; export type PeopleKind = "CONTRACTOR" | "EMPLOYEE" | "SERVICE_ACCOUNT"; export type UpdatePeopleInput = { additionalEmailAddresses?: ReadonlyArray | null | undefined; - expectedVersion: number; fullName?: string | null | undefined; id: string; kind?: PeopleKind | null | undefined; @@ -30,7 +29,6 @@ export type PeopleViewUpdatePeopleMutation$data = { readonly kind: PeopleKind; readonly primaryEmailAddress: string; readonly updatedAt: string; - readonly version: number; }; }; }; @@ -111,13 +109,6 @@ v1 = [ "kind": "ScalarField", "name": "updatedAt", "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "version", - "storageKey": null } ], "storageKey": null @@ -144,16 +135,16 @@ return { "selections": (v1/*: any*/) }, "params": { - "cacheID": "96bba526a231dd76ef58d32ec01aac3a", + "cacheID": "d09fc02a745b951e278cd492343e49f1", "id": null, "metadata": {}, "name": "PeopleViewUpdatePeopleMutation", "operationKind": "mutation", - "text": "mutation PeopleViewUpdatePeopleMutation(\n $input: UpdatePeopleInput!\n) {\n updatePeople(input: $input) {\n people {\n id\n fullName\n primaryEmailAddress\n additionalEmailAddresses\n kind\n updatedAt\n version\n }\n }\n}\n" + "text": "mutation PeopleViewUpdatePeopleMutation(\n $input: UpdatePeopleInput!\n) {\n updatePeople(input: $input) {\n people {\n id\n fullName\n primaryEmailAddress\n additionalEmailAddresses\n kind\n updatedAt\n }\n }\n}\n" } }; })(); -(node as any).hash = "957952927fbe2337a180599f34ce961c"; +(node as any).hash = "15fece3e846bd713533b9e91a1ceffe2"; export default node; diff --git a/apps/console/src/pages/organizations/policies/UpdatePolicyView.tsx b/apps/console/src/pages/organizations/policies/UpdatePolicyView.tsx index 9df0d0d28..8c2101b5a 100644 --- a/apps/console/src/pages/organizations/policies/UpdatePolicyView.tsx +++ b/apps/console/src/pages/organizations/policies/UpdatePolicyView.tsx @@ -30,7 +30,6 @@ const UpdatePolicyViewQuery = graphql` name content status - version reviewDate owner { id @@ -52,7 +51,6 @@ const UpdatePolicyMutation = graphql` name content status - version reviewDate owner { id @@ -115,7 +113,6 @@ function UpdatePolicyViewContent({ status, reviewDate: reviewDateValue, ownerId, - expectedVersion: data.policy.version!, }, }, onCompleted: (response, errors) => { diff --git a/apps/console/src/pages/organizations/policies/__generated__/UpdatePolicyViewMutation.graphql.ts b/apps/console/src/pages/organizations/policies/__generated__/UpdatePolicyViewMutation.graphql.ts index a0cc14cd6..99427d620 100644 --- a/apps/console/src/pages/organizations/policies/__generated__/UpdatePolicyViewMutation.graphql.ts +++ b/apps/console/src/pages/organizations/policies/__generated__/UpdatePolicyViewMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<9a97197f1f0eec8c00d0a58e0b07e8bc>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -12,7 +12,6 @@ import { ConcreteRequest } from 'relay-runtime'; export type PolicyStatus = "ACTIVE" | "DRAFT"; export type UpdatePolicyInput = { content?: string | null | undefined; - expectedVersion: number; id: string; name?: string | null | undefined; ownerId?: string | null | undefined; @@ -34,7 +33,6 @@ export type UpdatePolicyViewMutation$data = { }; readonly reviewDate: string | null | undefined; readonly status: PolicyStatus; - readonly version: number; }; }; }; @@ -103,13 +101,6 @@ v2 = [ "name": "status", "storageKey": null }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "version", - "storageKey": null - }, { "alias": null, "args": null, @@ -161,16 +152,16 @@ return { "selections": (v2/*: any*/) }, "params": { - "cacheID": "1ba586009eaf6e79dd2ab5862856cc63", + "cacheID": "aae664c7d961ad4e17c8e37464a34865", "id": null, "metadata": {}, "name": "UpdatePolicyViewMutation", "operationKind": "mutation", - "text": "mutation UpdatePolicyViewMutation(\n $input: UpdatePolicyInput!\n) {\n updatePolicy(input: $input) {\n policy {\n id\n name\n content\n status\n version\n reviewDate\n owner {\n id\n fullName\n }\n }\n }\n}\n" + "text": "mutation UpdatePolicyViewMutation(\n $input: UpdatePolicyInput!\n) {\n updatePolicy(input: $input) {\n policy {\n id\n name\n content\n status\n reviewDate\n owner {\n id\n fullName\n }\n }\n }\n}\n" } }; })(); -(node as any).hash = "47570dfcceba283c51a4ef2f88143d39"; +(node as any).hash = "d0f7b9d21b450416900ccb46439e2894"; export default node; diff --git a/apps/console/src/pages/organizations/policies/__generated__/UpdatePolicyViewQuery.graphql.ts b/apps/console/src/pages/organizations/policies/__generated__/UpdatePolicyViewQuery.graphql.ts index 6367d1cbd..6fd065815 100644 --- a/apps/console/src/pages/organizations/policies/__generated__/UpdatePolicyViewQuery.graphql.ts +++ b/apps/console/src/pages/organizations/policies/__generated__/UpdatePolicyViewQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -29,7 +29,6 @@ export type UpdatePolicyViewQuery$data = { }; readonly reviewDate?: string | null | undefined; readonly status?: PolicyStatus; - readonly version?: number; }; }; export type UpdatePolicyViewQuery = { @@ -93,13 +92,6 @@ v5 = { "name": "status", "storageKey": null }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "version", - "storageKey": null - }, { "alias": null, "args": null, @@ -328,16 +320,16 @@ return { ] }, "params": { - "cacheID": "16bd0bbc57d75f5c4d40eccd560b3da5", + "cacheID": "b7958bceabdd33c39fb403bdc6dbd85e", "id": null, "metadata": {}, "name": "UpdatePolicyViewQuery", "operationKind": "query", - "text": "query UpdatePolicyViewQuery(\n $policyId: ID!\n $organizationId: ID!\n) {\n policy: node(id: $policyId) {\n __typename\n id\n ... on Policy {\n name\n content\n status\n version\n reviewDate\n owner {\n id\n fullName\n }\n }\n }\n organization: node(id: $organizationId) {\n __typename\n ...PeopleSelector_organization\n id\n }\n}\n\nfragment PeopleSelector_organization on Organization {\n id\n peoples(first: 100, orderBy: {direction: ASC, field: FULL_NAME}) {\n edges {\n node {\n id\n fullName\n primaryEmailAddress\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n" + "text": "query UpdatePolicyViewQuery(\n $policyId: ID!\n $organizationId: ID!\n) {\n policy: node(id: $policyId) {\n __typename\n id\n ... on Policy {\n name\n content\n status\n reviewDate\n owner {\n id\n fullName\n }\n }\n }\n organization: node(id: $organizationId) {\n __typename\n ...PeopleSelector_organization\n id\n }\n}\n\nfragment PeopleSelector_organization on Organization {\n id\n peoples(first: 100, orderBy: {direction: ASC, field: FULL_NAME}) {\n edges {\n node {\n id\n fullName\n primaryEmailAddress\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n" } }; })(); -(node as any).hash = "1494ae295ead283c9784cc748c5536f7"; +(node as any).hash = "d29279c14662125a7fb14fa4da857f2b"; export default node; diff --git a/apps/console/src/pages/organizations/vendors/VendorView.tsx b/apps/console/src/pages/organizations/vendors/VendorView.tsx index 186f772c6..513c101db 100644 --- a/apps/console/src/pages/organizations/vendors/VendorView.tsx +++ b/apps/console/src/pages/organizations/vendors/VendorView.tsx @@ -36,7 +36,6 @@ const vendorViewQuery = graphql` privacyPolicyUrl createdAt updatedAt - version } } } @@ -57,7 +56,6 @@ const updateVendorMutation = graphql` termsOfServiceUrl privacyPolicyUrl updatedAt - version } } } @@ -145,7 +143,6 @@ function VendorViewContent({ variables: { input: { id: data.node.id, - expectedVersion: data.node.version, ...formattedData, }, }, @@ -176,7 +173,7 @@ function VendorViewContent({ } }, }); - }, [commit, data.node.id, data.node.version, formData, loadQuery, toast]); + }, [commit, data.node.id, formData, loadQuery, toast]); const handleFieldChange = (field: keyof typeof formData, value: unknown) => { setFormData((prev) => ({ diff --git a/apps/console/src/pages/organizations/vendors/__generated__/VendorViewQuery.graphql.ts b/apps/console/src/pages/organizations/vendors/__generated__/VendorViewQuery.graphql.ts index bb44b9402..fb96e8813 100644 --- a/apps/console/src/pages/organizations/vendors/__generated__/VendorViewQuery.graphql.ts +++ b/apps/console/src/pages/organizations/vendors/__generated__/VendorViewQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<<61507551e23b8cc70b401cab8ae4e575>> * @lightSyntaxTransform * @nogrep */ @@ -28,7 +28,6 @@ export type VendorViewQuery$data = { readonly statusPageUrl?: string | null | undefined; readonly termsOfServiceUrl?: string | null | undefined; readonly updatedAt?: string; - readonly version?: number; }; }; export type VendorViewQuery = { @@ -134,13 +133,6 @@ v13 = { "kind": "ScalarField", "name": "updatedAt", "storageKey": null -}, -v14 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "version", - "storageKey": null }; return { "fragment": { @@ -171,8 +163,7 @@ return { (v10/*: any*/), (v11/*: any*/), (v12/*: any*/), - (v13/*: any*/), - (v14/*: any*/) + (v13/*: any*/) ], "type": "Vendor", "abstractKey": null @@ -219,8 +210,7 @@ return { (v10/*: any*/), (v11/*: any*/), (v12/*: any*/), - (v13/*: any*/), - (v14/*: any*/) + (v13/*: any*/) ], "type": "Vendor", "abstractKey": null @@ -231,16 +221,16 @@ return { ] }, "params": { - "cacheID": "3569222e84bb1fa070b4ce2145d677ac", + "cacheID": "40428ff15eb094ffe4cb5ffb5d135cc1", "id": null, "metadata": {}, "name": "VendorViewQuery", "operationKind": "query", - "text": "query VendorViewQuery(\n $vendorId: ID!\n) {\n node(id: $vendorId) {\n __typename\n ... on Vendor {\n id\n name\n description\n serviceStartAt\n serviceTerminationAt\n serviceCriticality\n riskTier\n statusPageUrl\n termsOfServiceUrl\n privacyPolicyUrl\n createdAt\n updatedAt\n version\n }\n id\n }\n}\n" + "text": "query VendorViewQuery(\n $vendorId: ID!\n) {\n node(id: $vendorId) {\n __typename\n ... on Vendor {\n id\n name\n description\n serviceStartAt\n serviceTerminationAt\n serviceCriticality\n riskTier\n statusPageUrl\n termsOfServiceUrl\n privacyPolicyUrl\n createdAt\n updatedAt\n }\n id\n }\n}\n" } }; })(); -(node as any).hash = "9cdc25f48997786054c22246fb417db8"; +(node as any).hash = "dbef9acdc02dd7e8cd546c0bb8793b9a"; export default node; diff --git a/apps/console/src/pages/organizations/vendors/__generated__/VendorViewUpdateVendorMutation.graphql.ts b/apps/console/src/pages/organizations/vendors/__generated__/VendorViewUpdateVendorMutation.graphql.ts index b01088ed6..4d5d9e075 100644 --- a/apps/console/src/pages/organizations/vendors/__generated__/VendorViewUpdateVendorMutation.graphql.ts +++ b/apps/console/src/pages/organizations/vendors/__generated__/VendorViewUpdateVendorMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<8799e334baf7ee70b0a03519f6da8f97>> + * @generated SignedSource<<7568e87d53b6e76d1db4e3029430f118>> * @lightSyntaxTransform * @nogrep */ @@ -13,7 +13,6 @@ export type RiskTier = "CRITICAL" | "GENERAL" | "SIGNIFICANT"; export type ServiceCriticality = "HIGH" | "LOW" | "MEDIUM"; export type UpdateVendorInput = { description?: string | null | undefined; - expectedVersion: number; id: string; name?: string | null | undefined; privacyPolicyUrl?: string | null | undefined; @@ -41,7 +40,6 @@ export type VendorViewUpdateVendorMutation$data = { readonly statusPageUrl: string | null | undefined; readonly termsOfServiceUrl: string | null | undefined; readonly updatedAt: string; - readonly version: number; }; }; }; @@ -157,13 +155,6 @@ v1 = [ "kind": "ScalarField", "name": "updatedAt", "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "version", - "storageKey": null } ], "storageKey": null @@ -190,16 +181,16 @@ return { "selections": (v1/*: any*/) }, "params": { - "cacheID": "d6de753f4ecc8f59921e246e178bf8a0", + "cacheID": "1a49efe0fe5e3da519e1b15a8f81cc1e", "id": null, "metadata": {}, "name": "VendorViewUpdateVendorMutation", "operationKind": "mutation", - "text": "mutation VendorViewUpdateVendorMutation(\n $input: UpdateVendorInput!\n) {\n updateVendor(input: $input) {\n vendor {\n id\n name\n description\n serviceStartAt\n serviceTerminationAt\n serviceCriticality\n riskTier\n statusPageUrl\n termsOfServiceUrl\n privacyPolicyUrl\n updatedAt\n version\n }\n }\n}\n" + "text": "mutation VendorViewUpdateVendorMutation(\n $input: UpdateVendorInput!\n) {\n updateVendor(input: $input) {\n vendor {\n id\n name\n description\n serviceStartAt\n serviceTerminationAt\n serviceCriticality\n riskTier\n statusPageUrl\n termsOfServiceUrl\n privacyPolicyUrl\n updatedAt\n }\n }\n}\n" } }; })(); -(node as any).hash = "836cf8657449473503596456b8deb873"; +(node as any).hash = "15ffa38b13259f9c7c6511aa72d07247"; export default node; diff --git a/data/frameworks/ISO27001-2022.json b/data/frameworks/ISO27001-2022.json new file mode 100644 index 000000000..3e5ecc08f --- /dev/null +++ b/data/frameworks/ISO27001-2022.json @@ -0,0 +1,620 @@ +{ + "name": "ISO/IEC 27001:2022", + "controls": [ + { + "id": "C.4.1", + "name": "Understanding the organization and its context", + "description": "The organization shall determine external and internal issues that are relevant to its purpose and that affect its ability to achieve the intended outcome(s) of its information security management system." + }, + { + "id": "C.4.2", + "name": "Understanding the needs of interested parties", + "description": "The organization shall determine: \n\na) interested parties that are relevant to the information security management system; \n\nb) the relevant requirements of these interested parties; \n\nc) which of these requirements will be addressed through the information security management system." + }, + { + "id": "C.4.3", + "name": "Determining the scope of the information security management system", + "description": "The organization shall determine the boundaries and applicability of the information security management system to establish its scope. When determining this scope, the organization shall consider: \n\na) the external and internal issues referred to in 4.1; \n\nb) the requirements referred to in 4.2; \n\nc) interfaces and dependencies between activities performed by the organization, and those that are performed by other organizations. \n\nThe scope shall be available as documented information." + }, + { + "id": "C.4.4", + "name": "Information security management system", + "description": "The organization shall establish, implement, maintain and continually improve an information security management system, including the processes needed and their interactions, in accordance with the requirements of ISO 27001." + }, + { + "id": "C.5.1", + "name": "Leadership and commitment", + "description": "Top management shall demonstrate leadership and commitment with respect to the information security management system by: \n\na) ensuring the information security policy and the information security objectives are established and are compatible with the strategic direction of the organization; \n\nb) ensuring the integration of the information security management system requirements into the organization’s processes; \n\nc) ensuring that the resources needed for the information security management system are available; \n\nd) communicating the importance of effective information security management and of conforming to the information security management system requirements; \n\ne) ensuring that the information security management system achieves its intended outcome(s); \n\nf) directing and supporting persons to contribute to the effectiveness of the information security management system; \n\ng) promoting continual improvement; and \n\nh) supporting other relevant management roles to demonstrate their leadership as it applies to their areas of responsibility." + }, + { + "id": "C.5.2", + "name": "Policy", + "description": "Top management shall establish an information security policy that: \n\na) is appropriate to the purpose of the organization; \n\nb) includes information security objectives (see 6.2) or provides the framework for setting information security objectives; \n\nc) includes a commitment to satisfy applicable requirements related to information security; \n\nd) includes a commitment to continual improvement of the information security management system.\n\nThe information security policy shall: \n\ne) be available as documented information; \n\nf) be communicated within the organization; \n\ng) be available to interested parties, as appropriate." + }, + { + "id": "C.5.3", + "name": "Organizational roles, responsibilities and authorities", + "description": "Top management shall ensure that the responsibilities and authorities for roles relevant to information security are assigned and communicated within the organization. \n\nTop management shall assign the responsibility and authority for: \n\na) ensuring that the information security management system conforms to the requirements of this document; \n\nb) reporting on the performance of the information security management system to top management." + }, + { + "id": "C.6.1.1", + "name": "General actions to address risks and opportunities", + "description": "When planning for the information security management system, the organization shall consider the issues referred to in 4.1 and the requirements referred to in 4.2 and determine the risks and opportunities that need to be addressed to: \n\na) ensure the information security management system can achieve its intended outcome(s); \n\nb) prevent, or reduce, undesired effects; \n\nc) achieve continual improvement.\n\nThe organization shall plan: \n\nd) actions to address these risks and opportunities; and \n\ne) how to \n 1) integrate and implement the actions into its information security management system processes; and \n 2) evaluate the effectiveness of these actions" + }, + { + "id": "C.6.1.2", + "name": "Information security risk assessment", + "description": "The organization shall define and apply an information security risk assessment process that: \n\na) establishes and maintains information security risk criteria that include: \n 1) the risk acceptance criteria; and \n 2) criteria for performing information security risk assessments; \n\nb) ensures that repeated information security risk assessments produce consistent, valid and comparable results; \n\nc) identifies the information security risks: \n 1) apply the information security risk assessment process to identify risks associated with the loss of confidentiality, integrity and availability for information within the scope of the information security management system; and \n 2) identify the risk owners; \n\nd) analyses the information security risks: \n 1) assess the potential consequences that would result if the risks identified in 6.1.2 c) 1) were to materialize; \n 2) assess the realistic likelihood of the occurrence of the risks identified in 6.1.2 c) 1); and \n 3) determine the levels of risk; \n\ne) evaluates the information security risks: \n 1) compare the results of risk analysis with the risk criteria established in 6.1.2 a); and \n 2) prioritize the analysed risks for risk treatment. \n\nThe organization shall retain documented information about the information security risk assessment process." + }, + { + "id": "C.6.1.3", + "name": "Information security risk treatment", + "description": "The organization shall define and apply an information security risk treatment process to: \n\na) select appropriate information security risk treatment options, taking account of the risk assessment results; \n\nb) determine all controls that are necessary to implement the information security risk treatment option(s) chosen; \n\nc) compare the controls determined in 6.1.3 b) above with those in Annex A and verify that no necessary controls have been omitted; \n\nd) produce a Statement of Applicability that contains: \n\n— the necessary controls (see 6.1.3 b) and c)); \n\n — justification for their inclusion; \n\n — whether the necessary controls are implemented or not; and \n\n — the justification for excluding any of the Annex A controls. \n\ne) formulate an information security risk treatment plan; and \n\nf) obtain risk owners’ approval of the information security risk treatment plan and acceptance of the residual information security risks. \n\nThe organization shall retain documented information about the information security risk treatment process." + }, + { + "id": "C.6.2", + "name": "Information security objective and planning to achieve them", + "description": "The organization shall establish information security objectives at relevant functions and levels. The information security objectives shall:\n\na) be consistent with the information security policy; \n\nb) be measurable (if practicable); \n\nc) take into account applicable information security requirements, and results from risk assessment and risk treatment; \n\nd) be monitored; \n\ne) be communicated; \n\nf) be updated as appropriate; \n\ng) be available as documented information. \n\nThe organization shall retain documented information on the information security objectives. \n\nWhen planning how to achieve its information security objectives, the organization shall determine: \n\nh) what will be done; \n\ni) what resources will be required; \n\nj) who will be responsible; \n\nk) when it will be completed; and \n\nl) how the results will be evaluated." + }, + { + "id": "C.6.3", + "name": "Planning of Changes", + "description": "When the organization determines the need for changes to the information security management system, the changes shall be carried out in a planned manner." + }, + { + "id": "C.7.1", + "name": "Resources", + "description": "The organization shall determine and provide the resources needed for the establishment, implementation, maintenance and continual improvement of the information security management system." + }, + { + "id": "C.7.2", + "name": "Competence", + "description": "The organization shall:\n\na) determine the necessary competence of person(s) doing work under its control that affects its information security performance; \n\nb) ensure that these persons are competent on the basis of appropriate education, training, or experience; \n\nc) where applicable, take actions to acquire the necessary competence, and evaluate the effectiveness of the actions taken; and \n\nd) retain appropriate documented information as evidence of competence." + }, + { + "id": "C.7.3", + "name": "Awareness", + "description": "Persons doing work under the organization’s control shall be aware of:\n\na) the information security policy; \n\nb) their contribution to the effectiveness of the information security management system, including the benefits of improved information security performance; and \n\nc) the implications of not conforming with the information security management system requirements." + }, + { + "id": "C.7.4", + "name": "Communication", + "description": "The organization shall determine the need for internal and external communications relevant to the information security management system including: \n\na) on what to communicate;\n\nb) when to communicate;\n\nc) with whom to communicate;\n\nd) how to communicate." + }, + { + "id": "C.7.5.1", + "name": "Documented information", + "description": "The organization’s information security management system shall include: \n\na) documented information required by this document; and \n\nb) documented information determined by the organization as being necessary for the effectiveness of the information security management system." + }, + { + "id": "C.7.5.2", + "name": "Creating and Updating", + "description": "When creating and updating documented information the organization shall ensure appropriate:\n\na) identification and description (e.g. a title, date, author, or reference number);\n\nb) format (e.g. language, software version, graphics) and media (e.g. paper, electronic); and\n\nc) review and approval for suitability and adequacy." + }, + { + "id": "C.7.5.3", + "name": "Control of documented information", + "description": "Documented information required by the information security management system and by this document shall be controlled to ensure: \n\na) it is available and suitable for use, where and when it is needed; and\n\nb) it is adequately protected (e.g. from loss of confidentiality, improper use, or loss of integrity). \n\nFor the control of documented information, the organization shall address the following activities, as applicable: \n\nc) distribution, access, retrieval and use;\n\nd) storage and preservation, including the preservation of legibility;\n\ne) control of changes (e.g. version control); and\n\nf) retention and disposition. \n\nDocumented information of external origin, determined by the organization to be necessary for the planning and operation of the information security management system, shall be identified as appropriate, and controlled." + }, + { + "id": "C.8.1", + "name": "Operation planning and control", + "description": "The organization shall plan, implement and control the processes needed to meet requirements, and to implement the actions determined in Clause 6, by:\n\n— establishing criteria for the processes;\n\n— implementing control of the processes in accordance with the criteria. \n\nDocumented information shall be available to the extent necessary to have confidence that the processes have been carried out as planned. \n\nThe organization shall control planned changes and review the consequences of unintended changes, taking action to mitigate any adverse effects, as necessary. \n\nThe organization shall ensure that externally provided processes, products or services that are relevant to the information security management system are controlled." + }, + { + "id": "C.8.2", + "name": "Information security risk assessment", + "description": "The organization shall perform information security risk assessments at planned intervals or when significant changes are proposed or occur, taking account of the criteria established in 6.1.2 a). \n\nThe organization shall retain documented information of the results of the information security risk assessments." + }, + { + "id": "C.8.3", + "name": "Information security risk treatment", + "description": "The organization shall retain documented information of the results of the information security risk treatment." + }, + { + "id": "C.9.1", + "name": "Monitoring, measurement, analysis, and evaluation", + "description": "The organization shall determine:\n\na) what needs to be monitored and measured, including information security processes and controls; \n\nb) the methods for monitoring, measurement, analysis and evaluation, as applicable, to ensure valid results. The methods selected should produce comparable and reproducible results to be considered valid; \n\nc) when the monitoring and measuring shall be performed; \n\nd) who shall monitor and measure; \n\ne) when the results from monitoring and measurement shall be analyzed and evaluated; \n\nf) who shall analyze and evaluate these results. \n\nDocumented information shall be available as evidence of the results. \n\nThe organization shall evaluate the information security performance and the effectiveness of the information security management system." + }, + { + "id": "C.9.2.1", + "name": "Internal Audit - General", + "description": "The organization shall conduct internal audits at planned intervals to provide information on whether the information security management system: \n\na) conforms to\n 1) the organization’s own requirements for its information security management system; \n 2) the requirements of this document;\n\nb) is effectively implemented and maintained." + }, + { + "id": "C.9.2.2", + "name": "Internal Audit Program", + "description": "The organization shall plan, establish, implement and maintain an audit programme(s), including the frequency, methods, responsibilities, planning requirements and reporting. \n\nWhen establishing the internal audit programme(s), the organization shall consider the importance of the processes concerned and the results of previous audits. \n\nThe organization shall: \n\na) define the audit criteria and scope for each audit;\n \nb) select auditors and conduct audits that ensure objectivity and the impartiality of the audit process; \n\nc) ensure that the results of the audits are reported to relevant management; \n\nDocumented information shall be available as evidence of the implementation of the audit programme(s) and the audit results." + }, + { + "id": "C.9.3.1", + "name": "Management review - General", + "description": "Top management shall review the organization's information security management system at planned intervals to ensure its continuing suitability, adequacy and effectiveness." + }, + { + "id": "C.9.3.2", + "name": "Management review inputs", + "description": "The management review shall include consideration of: \n\na) the status of actions from previous management reviews; \n\nb) changes in external and internal issues that are relevant to the information security management system; \n\nc) changes in needs and expectations of interested parties that are relevant to the information security management system; \n\nd) feedback on the information security performance, including trends in: \n 1) nonconformities and corrective actions;\n 2) monitoring and measurement results;\n 3) audit results; \n 4) fulfilment of information security objectives;\n\ne) feedback from interested parties;\n\nf) results of risk assessment and status of risk treatment plan; \n\ng) opportunities for continual improvement." + }, + { + "id": "C.9.3.3", + "name": "Management review results", + "description": "The results of the management review shall include decisions related to continual improvement opportunities and any needs for changes to the information security management system.\n\nDocumented information shall be available as evidence of the results of management reviews." + }, + { + "id": "C.10.1", + "name": "Continual Improvement", + "description": "The organization shall continually improve the suitability, adequacy and effectiveness of the information security management system." + }, + { + "id": "C.10.2", + "name": "Nonconformity and corrective action", + "description": "When a Nonconformity occurs, the organization shall:\n\na) React to the nonconformity, and as applicable:\n 1) take action to control and correct it;\n 2) deal with the consequences\n\nb) evaluate the need for action to eliminate the causes of nonconformity, in order that it does not recur or occur elsewhere, by;\n 1) reviewing the nonconformity;\n 2) determining the causes of the nonconformity; and\n 3) determining if similar nonconformities exist, or could potentially occur\n\nc) implement any action needed;\n\nd) review the effectiveness of any corrective action taken; and\n\ne) make changes to the information security management system, if necessary. \n\nCorrective actions shall be appropriate to the effects of the nonconformities encountered.\n\nDocumented information shall be available as evidence of:\n\nf) The nature of the nonconformities and any subsequent actions taken,\n\ng) the results of any corrective action." + }, + { + "id": "A.5.1", + "name": "Policies for information security", + "description": "Information security policy and topic-specific policies shall be defined, approved by management, published, communicated to and acknowledged by relevant personnel and relevant interested parties, and reviewed at planned intervals and if significant changes occur." + }, + { + "id": "A.5.2", + "name": "Information security roles and responsibilities", + "description": "Information security roles and responsibilities shall be defined and allocated according to the organization needs." + }, + { + "id": "A.5.3", + "name": "Segregation of duties", + "description": "Conflicting duties and conflicting areas of responsibility shall be segregated." + }, + { + "id": "A.5.4", + "name": "Management responsibilities", + "description": "Management shall require all personnel to apply information security in accordance with the established information security policy, topic-specific policies and procedures of the organization." + }, + { + "id": "A.5.5", + "name": "Contact with authorities", + "description": "The organization shall establish and maintain contact with relevant authorities." + }, + { + "id": "A.5.6", + "name": "Contact with special interest groups", + "description": "The organization shall establish and maintain contact with special interest groups or other specialist security forums and professional associations." + }, + { + "id": "A.5.7", + "name": "Threat Intelligence", + "description": "Information relating to information security threats shall be collected and analyzed to produce threat intelligence." + }, + { + "id": "A.5.8", + "name": "Information security in project management", + "description": "Information security shall be integrated into project management." + }, + { + "id": "A.5.9", + "name": "Inventory of information and other associated assets", + "description": "An inventory of information and other associated assets, including owners, shall be developed and maintained." + }, + { + "id": "A.5.10", + "name": "Acceptable use of information and other associated assets", + "description": "Rules for the acceptable use and procedures for handling information and other associated assets shall be identified, documented and implemented." + }, + { + "id": "A.5.11", + "name": "Return of assets", + "description": "Personnel and other interested parties as appropriate shall return all the organization’s assets in their possession upon change or termination of their employment, contract or agreement." + }, + { + "id": "A.5.12", + "name": "Classification of information", + "description": "Information shall be classified according to the information security needs of the organization based on confidentiality, integrity, availability and relevant interested party requirements." + }, + { + "id": "A.5.13", + "name": "Labelling of information", + "description": "An appropriate set of procedures for information labelling shall be developed and implemented in accordance with the information classification scheme adopted by the organization." + }, + { + "id": "A.5.14", + "name": "Information transfer", + "description": "Information transfer rules, procedures, or agreements shall be in place for all types of transfer facilities within the organization and between the organization and other parties." + }, + { + "id": "A.5.15", + "name": "Access control", + "description": "Rules to control physical and logical access to information and other associated assets shall be established and implemented based on business and information security requirements." + }, + { + "id": "A.5.16", + "name": "Identity management", + "description": "The full life cycle of identities shall be managed." + }, + { + "id": "A.5.17", + "name": "Authentication information", + "description": "Allocation and management of authentication information shall be controlled by a management process, including advising personnel on the appropriate handling of authentication information." + }, + { + "id": "A.5.18", + "name": "Access rights", + "description": "Access rights to information and other associated assets shall be provisioned, reviewed, modified and removed in accordance with the organization’s topic-specific policy on and rules for access control." + }, + { + "id": "A.5.19", + "name": "Information security in supplier relationships", + "description": "Processes and procedures shall be defined and implemented to manage the information security risks associated with the use of supplier’s products or services." + }, + { + "id": "A.5.20", + "name": "Addressing information security within supplier agreements", + "description": "Relevant information security requirements shall be established and agreed with each supplier based on the type of supplier relationship." + }, + { + "id": "A.5.21", + "name": "Managing information security in the ICT supply chain", + "description": "Processes and procedures shall be defined and implemented to manage the information security risks associated with the ICT products and services supply chain." + }, + { + "id": "A.5.22", + "name": "Monitoring, review and change management of supplier services", + "description": "The organization shall regularly monitor, review, evaluate and manage change in supplier information security practices and service delivery." + }, + { + "id": "A.5.23", + "name": "Information security for use of cloud services", + "description": "Processes for acquisition, use, management and exit from cloud services shall be established in accordance with the organization’s information security requirements." + }, + { + "id": "A.5.24", + "name": "Information security incident management planning and preparation", + "description": "The organization shall plan and prepare for managing information security incidents by defining, establishing and communicating information security incident management processes, roles and responsibilities." + }, + { + "id": "A.5.25", + "name": "Assessment and decision on information security events", + "description": "The organization shall assess information security events and decide if they are to be categorized as information security incidents." + }, + { + "id": "A.5.26", + "name": "Response to information security incidents", + "description": "Information security incidents shall be responded to in accordance with the documented procedures." + }, + { + "id": "A.5.27", + "name": "Learning from information security incidents", + "description": "Knowledge gained from information security incidents shall be used to strengthen and improve the information security controls." + }, + { + "id": "A.5.28", + "name": "Collection of evidence", + "description": "The organization shall establish and implement procedures for the identification, collection, acquisition and preservation of evidence related to information security events." + }, + { + "id": "A.5.29", + "name": "Information security during disruption", + "description": "The organization shall plan how to maintain information security at an appropriate level during disruption." + }, + { + "id": "A.5.30", + "name": "ICT readiness for business continuity", + "description": "ICT readiness shall be planned, implemented, maintained and tested based on business continuity objectives and ICT continuity requirements." + }, + { + "id": "A.5.31", + "name": "Legal, statutory, regulatory and contractual requirements", + "description": "Legal, statutory, regulatory and contractual requirements relevant to information security and the organization’s approach to meet these requirements shall be identified, documented and kept up to date." + }, + { + "id": "A.5.32", + "name": "Intellectual property rights", + "description": "The organization shall implement appropriate procedures to protect intellectual property rights." + }, + { + "id": "A.5.33", + "name": "Protection of records", + "description": "Records shall be protected from loss, destruction, falsification, unauthorized access and unauthorized release." + }, + { + "id": "A.5.34", + "name": "Privacy and protection of PII", + "description": "The organization shall identify and meet the requirements regarding the preservation of privacy and protection of PII according to applicable laws and regulations and contractual requirements." + }, + { + "id": "A.5.35", + "name": "Independent review of information security", + "description": "The organization’s approach to managing information security and its implementation including people, processes and technologies shall be reviewed independently at planned intervals, or when significant changes occur." + }, + { + "id": "A.5.36", + "name": "Compliance with policies, rules and standards for information security", + "description": "Compliance with the organization’s information security policy, topic-specific policies, rules and standards shall be regularly reviewed." + }, + { + "id": "A.5.37", + "name": "Documented operating procedures", + "description": "Operating procedures for information processing facilities shall be documented and made available to personnel who need them." + }, + { + "id": "A.6.1", + "name": "Screening", + "description": "Background verification checks on all candidates to become personnel shall be carried out prior to joining the organization and on an ongoing basis taking into consideration applicable laws, regulations and ethics and be proportional to the business requirements, the classification of the information to be accessed and the perceived risks." + }, + { + "id": "A.6.2", + "name": "Terms and conditions of employment", + "description": "The employment contractual agreements shall state the personnel’s and the organization’s responsibilities for information security." + }, + { + "id": "A.6.3", + "name": "Information security awareness, education and training", + "description": "Personnel of the organization and relevant interested parties shall receive appropriate information security awareness, education and training and regular updates of the organization's information security policy, topic-specific policies and procedures, as relevant for their job function." + }, + { + "id": "A.6.4", + "name": "Disciplinary process", + "description": "A disciplinary process shall be formalized and communicated to take actions against personnel and other relevant interested parties who have committed an information security policy violation" + }, + { + "id": "A.6.5", + "name": "Responsibilities after termination or change of employment", + "description": "Information security responsibilities and duties that remain valid after termination or change of employment shall be defined, enforced and communicated to relevant personnel and other interested parties." + }, + { + "id": "A.6.6", + "name": "Confidentiality or non-disclosure agreements", + "description": "Confidentiality or non-disclosure agreements reflecting the organization’s needs for the protection of information shall be identified, documented, regularly reviewed and signed by personnel and other relevant interested parties." + }, + { + "id": "A.6.7", + "name": "Remote working", + "description": "Security measures shall be implemented when personnel are working remotely to protect information accessed, processed or stored outside the organization’s premises." + }, + { + "id": "A.6.8", + "name": "Information security event reporting", + "description": "The organization shall provide a mechanism for personnel to report observed or suspected information security events through appropriate channels in a timely manner." + }, + { + "id": "A.7.1", + "name": "Physical security perimeters", + "description": "Security perimeters shall be defined and used to protect areas that contain information and other associated assets." + }, + { + "id": "A.7.2", + "name": "Physical entry", + "description": "Secure areas shall be protected by appropriate entry controls and access points." + }, + { + "id": "A.7.3", + "name": "Securing offices, rooms and facilities", + "description": "Physical security for offices, rooms and facilities shall be designed and implemented." + }, + { + "id": "A.7.4", + "name": "Physical security monitoring", + "description": "Premises shall be continuously monitored for unauthorized physical access." + }, + { + "id": "A.7.5", + "name": "Protecting against physical and environmental threats", + "description": "Protection against physical and environmental threats, such as natural disasters and other intentional or unintentional physical threats to infrastructure shall be designed and implemented." + }, + { + "id": "A.7.6", + "name": "Working in secure areas", + "description": "Security measures for working in secure areas shall be designed and implemented." + }, + { + "id": "A.7.7", + "name": "Clear desk and clear screen", + "description": "Clear desk rules for papers and removable storage media and clear screen rules for information processing facilities shall be defined and appropriately enforced." + }, + { + "id": "A.7.8", + "name": "Equipment siting and protection", + "description": "Equipment shall be sited securely and protected." + }, + { + "id": "A.7.9", + "name": "Security of assets off-premises", + "description": "Off-site assets shall be protected." + }, + { + "id": "A.7.10", + "name": "Storage media", + "description": "Storage media shall be managed through their life cycle of acquisition, use, transportation and disposal in accordance with the organization’s classification scheme and handling requirements." + }, + { + "id": "A.7.11", + "name": "Supporting utilities", + "description": "Information processing facilities shall be protected from power failures and other disruptions caused by failures in supporting utilities." + }, + { + "id": "A.7.12", + "name": "Cabling security", + "description": "Cables carrying power, data or supporting information services shall be protected from interception, interference or damage." + }, + { + "id": "A.7.13", + "name": "Equipment maintenance", + "description": "Equipment shall be maintained correctly to ensure availability, integrity and confidentiality of information." + }, + { + "id": "A.7.14", + "name": "Secure disposal or re-use of equipment", + "description": "Items of equipment containing storage media shall be verified to ensure that any sensitive data and licensed software has been removed or securely overwritten prior to disposal or re-use." + }, + { + "id": "A.8.1", + "name": "User endpoint devices", + "description": "Information stored on, processed by or accessible via user endpoint devices shall be protected." + }, + { + "id": "A.8.2", + "name": "Privileged access rights", + "description": "The allocation and use of privileged access rights shall be restricted and managed." + }, + { + "id": "A.8.3", + "name": "Information access restriction", + "description": "Access to information and other associated assets shall be restricted in accordance with the established topic-specific policy on access control." + }, + { + "id": "A.8.4", + "name": "Access to source code", + "description": "Read and write access to source code, development tools and software libraries shall be appropriately managed." + }, + { + "id": "A.8.5", + "name": "Secure authentication", + "description": "Secure authentication technologies and procedures shall be implemented based on information access restrictions and the topic-specific policy on access control." + }, + { + "id": "A.8.6", + "name": "Capacity management", + "description": "The use of resources shall be monitored and adjusted in line with current and expected capacity requirements." + }, + { + "id": "A.8.7", + "name": "Protection against malware", + "description": "Protection against malware shall be implemented and supported by appropriate user awareness." + }, + { + "id": "A.8.8", + "name": "Management of technical vulnerabilities", + "description": "Information about technical vulnerabilities of information systems in use shall be obtained, the organization’s exposure to such vulnerabilities should be evaluated and appropriate measures should be taken." + }, + { + "id": "A.8.9", + "name": "Configuration management", + "description": "Configurations, including security configurations, of hardware, software, services and networks shall be established, documented, implemented, monitored and reviewed." + }, + { + "id": "A.8.10", + "name": "Information deletion", + "description": "Information stored in information systems, devices or in any other storage media shall be deleted when no longer required." + }, + { + "id": "A.8.11", + "name": "Data masking", + "description": "Data masking shall be used in accordance with the organization’s topic-specific policy on access control and other related topic-specific policies, and business requirements, taking applicable legislation into consideration." + }, + { + "id": "A.8.12", + "name": "Data leakage prevention", + "description": "Data leakage prevention measures shall be applied to systems, networks and any other devices that process, store or transmit sensitive information." + }, + { + "id": "A.8.13", + "name": "Information backup", + "description": "Backup copies of information, software and systems shall be maintained and regularly tested in accordance with the agreed topic-specific policy on backup." + }, + { + "id": "A.8.14", + "name": "Redundancy of information processing facilities", + "description": "Information processing facilities shall be implemented with redundancy sufficient to meet availability requirements." + }, + { + "id": "A.8.15", + "name": "Logging", + "description": "Logs that record activities, exceptions, faults and other relevant events shall be produced, stored, protected and analysed." + }, + { + "id": "A.8.16", + "name": "Monitoring activities", + "description": "Networks, systems and applications shall be monitored for anomalous behaviour and appropriate actions taken to evaluate potential information security incidents." + }, + { + "id": "A.8.17", + "name": "Clock synchronization", + "description": "The clocks of information processing systems used by the organization shall be synchronized to approved time sources." + }, + { + "id": "A.8.18", + "name": "Use of privileged utility programs", + "description": "The use of utility programs that can be capable of overriding system and application controls shall be restricted and tightly controlled." + }, + { + "id": "A.8.19", + "name": "Installation of software on operational systems", + "description": "Procedures and measures shall be implemented to securely manage software installation on operational systems." + }, + { + "id": "A.8.20", + "name": "Networks security", + "description": "Networks and network devices shall be secured, managed and controlled to protect information in systems and applications." + }, + { + "id": "A.8.21", + "name": "Security of network services", + "description": "Security mechanisms, service levels and service requirements of network services shall be identified, implemented and monitored." + }, + { + "id": "A.8.22", + "name": "Segregation of networks", + "description": "Groups of information services, users and information systems shall be segregated in the organization’s networks." + }, + { + "id": "A.8.23", + "name": "Web filtering", + "description": "Access to external websites shall be managed to reduce exposure to malicious content." + }, + { + "id": "A.8.24", + "name": "Use of cryptography", + "description": "Rules for the effective use of cryptography, including cryptographic key management, shall be defined and implemented." + }, + { + "id": "A.8.25", + "name": "Secure development life cycle", + "description": "Rules for the secure development of software and systems shall be established and applied." + }, + { + "id": "A.8.26", + "name": "Application security requirements", + "description": "Information security requirements shall be identified, specified and approved when developing or acquiring applications." + }, + { + "id": "A.8.27", + "name": "Secure system architecture and engineering principles", + "description": "Principles for engineering secure systems shall be established, documented, maintained and applied to any information system development activities." + }, + { + "id": "A.8.28", + "name": "Secure coding", + "description": "Secure coding principles shall be applied to software development." + }, + { + "id": "A.8.29", + "name": "Security testing in development and acceptance", + "description": "Security testing processes shall be defined and implemented in the development life cycle." + }, + { + "id": "A.8.30", + "name": "Outsourced development", + "description": "The organization shall direct, monitor and review the activities related to outsourced system development." + }, + { + "id": "A.8.31", + "name": "Separation of development, test and production environments", + "description": "Development, testing and production environments shall be separated and secured." + }, + { + "id": "A.8.32", + "name": "Change management", + "description": "Changes to information processing facilities and information systems shall be subject to change management procedures." + }, + { + "id": "A.8.33", + "name": "Test information", + "description": "Test information shall be appropriately selected, protected and managed." + }, + { + "id": "A.8.34", + "name": "Protection of information systems during audit testing", + "description": "Audit tests and other assurance activities involving assessment of operational systems shall be planned and agreed between the tester and appropriate management." + } + ] +} diff --git a/data/frameworks/SOC2.json b/data/frameworks/SOC2.json new file mode 100644 index 000000000..ac44485ac --- /dev/null +++ b/data/frameworks/SOC2.json @@ -0,0 +1,249 @@ +{ + "name": "SOC2", + "controls": [ + { + "id": "CC1.1", + "name": "COSO Principle 1: The entity demonstrates a commitment to integrity and ethical values." + }, + { + "id": "CC1.2", + "name": "COSO Principle 2: The board of directors demonstrates independence from management and exercises oversight of the development and performance of internal control." + }, + { + "id": "CC1.3", + "name": "COSO Principle 3: Management establishes, with board oversight, structures, reporting lines, and appropriate authorities and responsibilities in the pursuit of objectives." + }, + { + "id": "CC1.4", + "name": "COSO Principle 4: The entity demonstrates a individuals in alignment with objectives." + }, + { + "id": "CC1.5", + "name": "COSO Principle 5: The entity holds individuals in the pursuit of objectives." + }, + { + "id": "CC2.1", + "name": "COSO Principle 13: The entity obtains or generates functioning of internal control." + }, + { + "id": "CC2.2", + "name": "COSO Principle 14: The entity internally communicates information, including objectives and responsibilities for internal control, necessary to support the functioning of internal control." + }, + { + "id": "CC2.3", + "name": "COSO Principle 15: The entity communicates with functioning of internal control." + }, + { + "id": "CC3.1", + "name": "COSO Principle 6: The entity specifies objectives with assessment of risks relating to objectives." + }, + { + "id": "CC3.2", + "name": "COSO Principle 7: The entity identifies risks to the achievement of its objectives across the entity and analyzes risks as a basis for determining how the risks should be managed." + }, + { + "id": "CC3.3", + "name": "COSO Principle 8: The entity considers the potential objectives." + }, + { + "id": "CC3.4", + "name": "COSO Principle 9: The entity identifies and assesses internal control." + }, + { + "id": "CC4.1", + "name": "COSO Principle 16: The entity selects, develops, and performs ongoing and/or separate evaluations to ascertain whether the components of internal control are present and functioning." + }, + { + "id": "CC4.2", + "name": "COSO Principle 17: The entity evaluates and communicates internal control deficiencies in a timely corrective action, including senior management and the board of directors, as appropriate." + }, + { + "id": "CC5.1", + "name": "COSO Principle 10: The entity selects and develops control activities that contribute to the mitigation of risks to the achievement of objectives to acceptable levels." + }, + { + "id": "CC5.2", + "name": "COSO Principle 11: The entity also selects and support the achievement of objectives." + }, + { + "id": "CC5.3", + "name": "COSO Principle 12: The entity deploys control activities through policies that establish what is expected and in procedures that put policies into action." + }, + { + "id": "CC6.1", + "name": "The entity implements logical access security software, infrastructure, and architectures over protected information assets to protect them from security events to meet the entity’s objectives." + }, + { + "id": "CC6.2", + "name": "Prior to issuing system credentials and granting system access, the entity registers and authorizes new internal and external users whose access is administered by the entity. For those users whose access is administered by the entity, user system credentials are removed when user access is no longer authorized." + }, + { + "id": "CC6.3", + "name": "The entity authorizes, modifies, or removes access to data, software, functions, and other protected information assets based on roles, responsibilities, or the system design and changes, giving consideration to the concepts of least privilege and segregation of duties, to meet the entity’s objectives." + }, + { + "id": "CC6.4", + "name": "The entity restricts physical access to facilities and protected information assets (for example, data center locations) to authorized personnel to meet the entity’s objectives." + }, + { + "id": "CC6.5", + "name": "The entity discontinues logical and physical protections over physical assets only after the ability to read or diminished and is no longer required to meet the entity’s objectives." + }, + { + "id": "CC6.6", + "name": "The entity implements logical access security measures system boundaries." + }, + { + "id": "CC6.7", + "name": "The entity restricts the transmission, movement, and removal of information to authorized internal and transmission, movement, or removal to meet the entity’s objectives." + }, + { + "id": "CC6.8", + "name": "The entity implements controls to prevent or detect and software to meet the entity’s objectives." + }, + { + "id": "CC7.1", + "name": "To meet its objectives, the entity uses detection and monitoring procedures to identify (1) changes to vulnerabilities, and (2) susceptibilities to newly discovered vulnerabilities." + }, + { + "id": "CC7.2", + "name": "The entity monitors system components and the operation of those components for anomalies that are indicative of malicious acts, natural disasters, and errors affecting the entity’s ability to meet its objectives; anomalies are analyzed to determine whether they represent security events." + }, + { + "id": "CC7.3", + "name": "The entity evaluates security events to determine whether they could or have resulted in a failure of the entity to meet its objectives (security incidents) and, if so, takes actions to prevent or address such failures." + }, + { + "id": "CC7.4", + "name": "The entity responds to identified security incidents by executing a defined incident response program to understand, contain, remediate, and communicate security incidents, as appropriate." + }, + { + "id": "CC7.5", + "name": "The entity identifies, develops, and implements activities to recover from identified security incidents." + }, + { + "id": "CC8.1", + "name": "The entity authorizes, designs, develops or acquires, configures, documents, tests, approves, and implements changes to infrastructure, data, software, and procedures to meet its objectives." + }, + { + "id": "CC9.1", + "name": "The entity identifies, selects, and develops risk business disruptions." + }, + { + "id": "CC9.2", + "name": "The entity assesses and manages risks associated with vendors and business partners." + }, + { + "id": "A1.1", + "name": "The entity maintains, monitors, and evaluates current processing capacity and use of system components capacity demand and to enable the implementation of additional capacity to help meet its objectives." + }, + { + "id": "A1.2", + "name": "The entity authorizes, designs, develops or acquires, implements, operates, approves, maintains, and back-up processes, and recovery infrastructure to meet its objectives." + }, + { + "id": "A1.3", + "name": "The entity tests recovery plan procedures supporting system recovery to meet its objectives." + }, + { + "id": "C1.1", + "name": "The entity identifies and maintains confidential confidentiality." + }, + { + "id": "C1.2", + "name": "The entity disposes of confidential information to meet the entity’s objectives related to confidentiality." + }, + { + "id": "PI1.1", + "name": "The entity obtains or generates, uses, and communicates relevant, quality information regarding the objectives processed and product and service specifications, to support the use of products and services." + }, + { + "id": "PI1.2", + "name": "The entity implements policies and procedures over system inputs, including controls over completeness and accuracy, to result in products, services, and reporting to meet the entity’s objectives." + }, + { + "id": "PI1.3", + "name": "The entity implements policies and procedures over reporting to meet the entity’s objectives." + }, + { + "id": "PI1.4", + "name": "The entity implements policies and procedures to make available or deliver output completely, accurately, and timely in accordance with specifications to meet the entity’s objectives." + }, + { + "id": "PI1.5", + "name": "The entity implements policies and procedures to store inputs, items in processing, and outputs completely, accurately, and timely in accordance with system specifications to meet the entity’s objectives." + }, + { + "id": "P1.1", + "name": "The entity provides notice to data subjects about its privacy practices to meet the entity’s objectives related to privacy. The notice is updated and communicated to entity’s privacy practices, including changes in the use of personal information, to meet the entity’s objectives related to privacy." + }, + { + "id": "P2.1", + "name": "The entity communicates choices available regarding the collection, use, retention, disclosure, and disposal of personal information to the data subjects and the consequences, if any, of each choice. Explicit consent for the collection, use, retention, disclosure, and disposal of personal information is obtained from data subjects or other authorized persons, if required. Such consent is obtained only for the intended purpose of the information to meet the entity’s objectives related to privacy. The entity’s basis for determining implicit consent for the collection, use, retention, disclosure, and disposal of personal information is documented." + }, + { + "id": "P3.1", + "name": "Personal information is collected consistent with the entity’s objectives related to privacy." + }, + { + "id": "P3.2", + "name": "For information requiring explicit consent, the entity communicates the need for such consent, as well as the consequences of a failure to provide consent for the request for personal information, and obtains the consent prior to the collection of the information to meet the entity’s objectives related to privacy." + }, + { + "id": "P4.1", + "name": "The entity limits the use of personal information to the privacy." + }, + { + "id": "P4.2", + "name": "The entity retains personal information consistent with the entity’s objectives related to privacy." + }, + { + "id": "P4.3", + "name": "The entity securely disposes of personal information to meet the entity’s objectives related to privacy." + }, + { + "id": "P5.1", + "name": "The entity grants identified and authenticated data subjects the ability to access their stored personal information for review and, upon request, provides physical or electronic copies of that information to data subjects to meet the entity’s objectives related to privacy. If access is denied, data subjects are informed of the denial and reason for such denial, as required, to meet the entity’s objectives related to privacy." + }, + { + "id": "P5.2", + "name": "The entity corrects, amends, or appends personal information based on information provided by data subjects and communicates such information to third parties, as committed or required, to meet the entity’s objectives related to privacy. If a request for correction is denied, data subjects are informed of the denial and reason for such denial to meet the entity’s objectives related to privacy." + }, + { + "id": "P6.1", + "name": "The entity discloses personal information to third parties with the explicit consent of data subjects, and such consent is obtained prior to disclosure to meet the entity’s objectives related to privacy." + }, + { + "id": "P6.2", + "name": "The entity creates and retains a complete, accurate, and timely record of authorized disclosures of personal information to meet the entity’s objectives related to privacy." + }, + { + "id": "P6.3", + "name": "The entity creates and retains a complete, accurate, and timely record of detected or reported unauthorized information to meet the entity’s objectives related to privacy." + }, + { + "id": "P6.4", + "name": "The entity obtains privacy commitments from vendors and other third parties who have access to personal information to meet the entity’s objectives related to privacy. The entity assesses those parties’ compliance on a periodic and as-needed basis and takes corrective action, if necessary." + }, + { + "id": "P6.5", + "name": "The entity obtains commitments from vendors and other third parties with access to personal information to notify the entity in the event of actual or suspected unauthorized disclosures of personal information. Such notifications are reported to appropriate personnel and acted on in accordance with established incident response procedures to meet the entity’s objectives related to privacy." + }, + { + "id": "P6.6", + "name": "The entity provides notification of breaches and others to meet the entity’s objectives related to privacy." + }, + { + "id": "P6.7", + "name": "The entity provides data subjects with an accounting of the personal information held and disclosure of the subjects’ request, to meet the entity’s objectives related to privacy." + }, + { + "id": "P7.1", + "name": "The entity collects and maintains accurate, up-to-date, the entity’s objectives related to privacy." + }, + { + "id": "P8.1", + "name": "The entity implements a process for receiving, addressing, resolving, and communicating the resolution of inquiries, complaints, and disputes from data subjects and others and periodically monitors compliance to meet the entity’s objectives related to privacy. Corrections and other necessary actions related to identified deficiencies are made or taken in a timely manner." + } + ] +} diff --git a/data/frameworks/soc2.yaml b/data/frameworks/soc2.yaml deleted file mode 100644 index 2a58a7882..000000000 --- a/data/frameworks/soc2.yaml +++ /dev/null @@ -1,365 +0,0 @@ -framework: - name: soc2 - description: SOC 2 - content-ref: "8d133718-f74b-4393-b072-e5fc89bcb952" - controls: - - content-ref: "44be4eb5-ae65-4c84-b7ac-083979af3f64" - category: "Secure your offices and internet access" - importance: "MANDATORY" - standards: ["CC 6.4"] - name: "Implement Physical Access Control" - description: | - Even if you do most of your business online, you still have offices, computers or even printed document: you need to make sure those assets are physically secured (we will focus on the digital access later). - - content-ref: "c878ed70-01d3-4796-8572-0228ff6dda0c" - category: "Secure your offices and internet access" - name: "Use VPNs to Secure Access for Remote Devices" - importance: "PREFERRED" - standards: ["CC 6.4", "CC 6.6", "CC 6.7"] - description: | - In short, implementing a VPN will encrypt your data and ensure a safe transmission between your employees devices and your internal network (even when using untrusted networks like a public wifi) ⇒ it offers a layer of security for your data. - - content-ref: "4ebc26df-6a3e-47fa-8adb-42fb8f0f5703" - category: "Secure your offices and internet access" - name: "Cloud datacenter physical security" - importance: "PREFERRED" - standards: ["CC 6.4"] - description: | - You know how critical a data center is. You don’t want unauthorized people to access it. - - content-ref: "23590469-5569-4d13-8f13-89de55f7cc77" - category: "Secure your offices and internet access" - name: "Implement Visitor Access Policies" - importance: "MANDATORY" - standards: ["CC 6.4"] - description: | - Even if you do most of your business online, you still have offices, computers or even printed document: you need to make sure those assets are physically secured (we will focus on the digital access later) regarding visitors. - - content-ref: "07402b58-762f-4a8d-a08b-dfddf7beca6c" - category: "Manage your computers" - name: "Protect your employees devices" - importance: "MANDATORY" - standards: - ["CC 2.1", "CC 6.1", "CC 6.6", "CC 6.7", "CC 6.8", "CC 7.1", "CC 7.2"] - description: | - Mobile devices often have access to sensitive company data ⇒ you need to ensure that all devices are secure and compliant with established security policies. - - content-ref: "edef4ee7-01bf-43df-a815-ce1862e6fef1" - category: "Set up your employees for success" - name: "Integrate security checklist in your onboarding process" - importance: "MANDATORY" - standards: ["CC 1.4", "CC 5.3"] - description: | - It is the perfect timing to ensure that every employees has: - - - accepted and signed all documents - - the access needed to perform his/her tasks - - started his/her security training - - content-ref: "578170a0-e0a5-47ef-9fdb-6971deee2958" - category: "Set up your employees for success" - name: "Properly off-board your employees" - importance: "MANDATORY" - standards: ["CC 5.3", "CC 6.2", "CC 6.5"] - description: | - Yes, people will leave your company (either by your decision or theirs). And you want to be prepare! If an early employee leaves and you forgot to change the ownership on his/her document, you might lose the documents. - - Also, you want to be sure people can’t access the company data or systems once they left! - - content-ref: "03e71306-95dc-4e59-9e35-71dba7b2a831" - category: "Set up your employees for success" - name: "Know your recruits" - importance: "MANDATORY" - standards: ["CC 1.4"] - description: | - When recruiting someone, you want to be sure of who you are hiring: by performing reference checks (it can also be background checks), you add an additional layer of certainty on the candidate by looking for potential red flags in the candidate’s past (history of unethical behavior, harassment, fraud, etc..). - - content-ref: "1bf9192a-1b6a-48da-b232-a6e01c0c4e06" - category: "Set up your employees for success" - name: "Train your employees on security" - importance: "PREFERRED" - standards: ["CC 1.4", "CC 2.2"] - description: | - Your employees are the main target of cyber threats (especially phishing and social engineering), and education is one of the best way to reduce risk. Awareness of your employees will improve your company security. - - content-ref: "0296ff7e-f57a-4e68-abad-1146b7b8311b" - category: "Set up your employees for success" - name: "Implement confidential whistleblower process" - importance: "ADVANCED" - standards: ["CC 2.2"] - description: | - It encourages and enables employees to raise serious concerns (violations of your code of ethics or law or regulations) in order for them to be addressed and corrected while being protected from any retaliation. - - content-ref: "dfd5fa7a-db83-4c34-b21f-846e7775c594" - category: "Set up your employees for success" - name: "Run performance reviews" - importance: "PREFERRED" - standards: ["CC 1.3", "CC 1.4", "CC 1.5", "CC 4.2", "CC 5.3"] - description: | - Makes sure your team has the skills and focus needed to protect what matters most in your business. They help spot training gaps, reinforce accountability and ensure everyone is aligned with your operational goals - security being one of them. It's all about building a culture that proactively minimizes risks while continuously improving. - - content-ref: "ce18a430-13af-4c76-9e43-816877e82ddd" - category: "Set up your employees for success" - name: "Specify security responsabilities" - importance: "MANDATORY" - standards: ["CC 1.3", "CC 1.4", "CC 2.2", "CC 5.3", "CC 1.2"] - description: | - Having clear ownership improve accountability, it helps employees figure out what is legit and what is not. - - content-ref: "08df0530-0d7e-430c-9a1d-c97db46e2c66" - category: "Secure your emails" - name: "Implement email filtering and warning system" - importance: "MANDATORY" - standards: ["CC 6.1", "CC 6.8"] - description: | - As you know ⇒ email is the most common entry point. Each phishing email filtered out of your employee mailbox is less mental load for you and your employees and less risk faced by your company. - - content-ref: "25fa20e1-0772-4103-8d57-24da3d59f7d1" - category: "Secure your emails" - name: "Implement email authentication (DMARC)" - importance: "PREFERRED" - standards: ["CC 6.8", "CC 7.2"] - description: | - DMARK (Domain-based Message Authentication, Reporting, and Conformance) helps prevent email spoofing and phishing attacks by verifying the authenticity of emails sent from your domain. - - It will do two things: - - - your recipient will know for sure it is coming from you and it will protect your company reputation (with gmail and cie). - - you will be sure (if activated) that the email you received is really from the said company (if they parametered DMARC as well) - - content-ref: "54dcea24-d714-46a7-8e8e-3967a8dad962" - category: "Secure your emails" - name: "Quarantine suspicious emails" - importance: "ADVANCED" - standards: ["CC 6.8", "CC 7.2"] - description: | - If you set up email quarantine in Google Workspace, you prevent potentially harmful emails from reaching your employees. By following the steps below, you can create customized rules to catch and review spam, phishing attempts, and malicious content, ensuring a more secure email environment. - - content-ref: "e8c50ebe-a395-40df-b1d6-54d4020babcc" - category: "Configure your system access" - name: "Enable 2FA on critical services" - importance: "MANDATORY" - standards: ["CC 6.1", "CC 6.8"] - description: | - In order to minimize the threat of someone getting access to something they should not, we follow the **secure principle:** nobody can easily get access to data/systems. - - We need to ensure that if someone has access to your password, they still cannot log into your account. Multi-Factor Authentication (MFA) adds extra layers of security by requiring users to provide additional authentication factors beyond their passwords. The most standard solution is 2FA: your password + something else. - - content-ref: "51e1ae1f-fe5b-4c65-bc0f-cd90a156437e" - category: "Configure your system access" - name: "Setup slack channel for access request" - importance: "MANDATORY" - standards: ["CC 6.2", "CC 6.3"] - description: | - In order to minimize the threat of someone getting access to something they should not, we follow the continuous update principle: making sure the privileges are up to date. We need to setup a proper process to define how we grant and revoke access to different systems. - - content-ref: "4f2b2e96-50a2-465f-8c31-85db878a402c" - category: "Configure your system access" - name: "Setup a password manager" - importance: "PREFERRED" - standards: ["CC 6.1"] - description: | - In order to minimize the threat of someone getting access to something they should not, we want to ensure a few things: - - - The password used in your company are complex enough - - They are stored encrypted - - They are shared safely when needed (not openly, on slack or by text) - - They are not compromised - - The easiest way to implement those is to use a password manager. - - content-ref: "077349b3-5df1-44eb-9c8e-3b3d291c0199" - category: "Configure your system access" - name: "Setup role based access (RBAC)" - importance: "PREFERRED" - standards: ["CC 2.1", "CC 6.1"] - description: | - In order to minimize the threat of someone getting access to something they should not, we follow the least privilege principle: access is limited to what's necessary for job duties. We need to define which function should have access to which tool in your company to serve as a reference when providing access to people (password manager vaults etc.). - - content-ref: "0245ebe5-7108-4fc6-b64f-96e0e0b24f67" - category: "Configure your system access" - name: "Enforce SSO when possible" - importance: "PREFERRED" - standards: ["CC 6.1"] - description: | - To minimize the risk of unauthorized access, it's important to centralize and secure authentication across your organization. Single Sign-On (SSO) enhances security by enabling better control over account access, enforcing consistent security policies (e.g., 2FA), and making it easier to revoke access when someone leaves the organization. - - content-ref: "66fabe67-82d1-4957-af81-c4b89485212a" - category: "Secure your codebase" - name: "Require Pull Requests (PRs)" - importance: "MANDATORY" - standards: ["CC 1.4", "CC 8.1", "CC 5.2"] - description: | - Requiring pull requests and code reviews ensures higher code quality and security by allowing multiple team members to catch bugs, inefficiencies, and potential vulnerabilities before code is merged. It also promotes collaboration, knowledge sharing, and accountability within the team. This process helps prevent issues in production and maintains adherence to coding standards. - - content-ref: "a1fcbc72-de61-47c1-b65d-cbc7c75c158d" - category: "Secure your codebase" - name: "Set-up dependancy vulnerability alerts" - importance: "MANDATORY" - standards: ["CC 4.1", "CC 8.1"] - description: | - It ensures your project stays secure and up-to-date without manual tracking of dependencies. It also reduces the risk of using outdated or insecure libraries in your codebase. - - content-ref: "2bb3102d-6bc7-4869-a86d-3ee2e6cb7756" - category: "Secure your codebase" - name: "Set-up 2FA for all contributors" - importance: "MANDATORY" - standards: ["CC 5.2"] - description: | - It reduces the risk of account compromise for all contributors, especially the ones not in your company. - - content-ref: "515c6c80-2274-40f0-82cd-cc912a2be1f9" - category: "Secure your codebase" - name: "Enable code scanning" - importance: "PREFERRED" - standards: ["CC 4.1", "CC 8.1"] - description: | - It ensures that potential security flaws are detected early. This proactive approach strengthens your security posture and helps maintain high code quality. - - content-ref: "f88963fe-2641-47b7-8a03-f18704952024" - category: "Secure your codebase" - name: "Document your development lifecycle" - importance: "PREFERRED" - standards: ["CC 1.4", "CC 5.2", "CC 8.1"] - description: | - Formalizing a proper development lifecycle helps your engineer in their jobs and helps you to scale your team. It reduces the chances of human error. - - content-ref: "8a5d46b7-ece7-4f84-b720-ccf88c5d49f8" - category: "Secure your infrastructure" - name: "List of your assets" - importance: "MANDATORY" - standards: ["CC 2.1", "CC 4.1"] - description: | - The assets that are storing or processing data are potential vulnerabilities that could be exploited. ⇒ You need to know which assets your company has in order to be able to properly manage and secure them. - - content-ref: "6a8a1745-850b-44ee-a688-d3e126ad46fa" - category: "Secure your infrastructure" - name: "Scan for Security updates" - importance: "MANDATORY" - standards: ["CC 4.1", "CC 6.8", "CC 7.1"] - description: | - An automated security scanning software on your infrastructure components enables you to fix potential vulnerabilities as soon as they are uncovered. - - content-ref: "5c18790f-f0f6-41ce-ae4b-6f6b3bcb7058" - category: "Secure your infrastructure" - name: "Implement IAM for database authentication" - importance: "PREFERRED" - standards: ["CC 6.1"] - description: | - You want to ensure that only the proper people can access your production database. You also want to avoid the burden of managing another list of users or rotating credentials. - - content-ref: "3274441b-b8b9-44ea-bd2a-bc2652b48973" - category: "Secure your infrastructure" - name: "Plug GCP & Github" - importance: "MANDATORY" - standards: ["CC 6.1", "CC 6.8 6.3"] - description: | - Having credentials to manage can become a nightmare (especially if you need to change/rotate them often) - it quickly becomes time consuming. It is the same with lifetime tokens - someone leaving and you need to change them. - - content-ref: "b0c11cc1-07f4-43ab-badb-e6ebf0cd9152" - category: "Secure your infrastructure" - name: "Manage your service accounts" - importance: "MANDATORY" - standards: ["CC 6.3"] - description: | - You don’t want to use the “default” service account, it has way too many permissions - it is a super administrator. Even GCP is asking you not to use it, it is only here for legacy reasons. In the same idea, grant your service accounts only what is necessary. - - content-ref: "cd865af6-0ab8-4366-b4eb-fb84baf03a87" - category: "Secure your infrastructure" - name: "Keep an history of all your changes" - importance: "MANDATORY" - standards: ["CC 7.1", "CC 7.2", "CC 8.1", "CC 5.3"] - description: | - Infrastructure as Code (IaC) makes sure all your infrastructure changes are done in a standardized and repeatable process - the chance of human error is lower. Moreover, IaC enables version control and peer reviews. When growing, you will have to do it - the earlier the better (later, it can become really painful). - - content-ref: "2553b667-5abd-426f-adc4-e9824e8d0994" - category: "Secure your infrastructure" - name: "Streamline your patch management" - importance: "MANDATORY" - standards: ["CC 7.1"] - description: | - New OS version vulnerabilities are regularly discovered - not applying patches for those leaves your application “open” to a publicly known vulnerability. If you don’t streamline the process, it will requires investment on your side and increase the exposure duration. - - content-ref: "c152fa68-0c76-405c-b07c-8fb413af06d0" - category: "Protect your Network" - name: "Restrict public access on your infrastructure" - importance: "MANDATORY" - standards: ["CC 6.1", "CC 6.6", "CC 6.7"] - description: | - Public access to your company's infrastructure is a serious security risk. By configuring your cloud provider to restrict public access, you can reduce the risk of unauthorized access to your sensitive data and systems. - - content-ref: "f68a9024-44b1-4bdd-b659-b086ff5865a4" - category: "Protect your Network" - name: "Set-up a WAF" - importance: "PREFERRED" - standards: ["CC 6.1", "CC 6.6"] - description: | - Cloudflare filters malicious traffic, preventing attacks (like SQL injections and cross-site scripting) and mitigates risks of service disruptions. Additionally, Cloudflare can masks your origin IP, reducing exposure and limiting direct access to your infrastructure. - - content-ref: "845e0d1e-ee55-4b7d-80dd-ea333818e61e" - category: "Protect your Network" - name: "Run a penetration test" - importance: "ADVANCED" - standards: ["CC 3.2", "CC 4.1", "CC 8.1"] - description: | - It is a good way to safeguard your company’s data. It is a real-life simulation of attack scenarios on your company: it enables you to identify potential vulnerabilities that can be fixed before being exploited. - - content-ref: "1c3819b4-3f2c-4480-ad97-2a7b0283f8d8" - category: "Safeguard your data" - name: "Automated backup" - importance: "MANDATORY" - standards: ["CC 7.5", "CC 9.1"] - description: | - Automated backups ensure that your data - one of your company’s most important assets - is regularly and securely saved, reducing the risk of data loss. The back-up keep it safe and available, the automated and regular allows for a restoration just before any disruption, minimizing loss. - - content-ref: "f3395da7-b717-46b4-8092-9fca304c8344" - category: "Safeguard your data" - name: "Help the auditor with a data-flow diagram" - importance: "MANDATORY" - standards: ["CC 2.1", "CC 2.2"] - description: | - The auditor doesn’t know your company and your systems. Providing him with a diagram that show all account data accross your systems & networks will enable him/her to understand your challenges faster and also to better evaluate your tradeoffs (when needed). It will also help you be 100% clear on how you process your data, and help with onboarding. - - content-ref: "5d9fb42c-7d7b-466a-85b1-afb535dbf4a6" - category: "Safeguard your data" - name: "Encrypt your data storage" - importance: "MANDATORY" - standards: ["CC 6.1", "CC 6.6", "CC 6.7"] - description: | - By encrypting data, you ensure that loss or theft of a device won’t result in a data breach. It also protects sensitive information from unauthorized access. - - content-ref: "44816697-458b-4768-9821-1e90325f0558" - category: "Safeguard your data" - name: "Encrypt your data in transit" - importance: "MANDATORY" - standards: ["CC 6.1", "CC 6.6", "CC 6.7"] - description: | - Data sent over networks can be intercepted and read - the encryption ensure it won’t be intelligible. It secure it from potential threats. - - content-ref: "0c650a10-8407-4035-bcb1-6f4cd035122b" - category: "Safeguard your data" - name: "Data inventory" - importance: "MANDATORY" - standards: ["CC 2.1", "CC 6.1", "CC 9.2"] - description: | - Having an accurate and up-to-date inventory of all your data assets (databases, cloud storage or even file shares) enables you to be exhaustive in how you treat your data safety. - - content-ref: "32d8584d-d171-43e2-8428-b52cf0b5ad0d" - category: "Log collection and monitoring" - name: "Configure logs and implement real-time monitoring" - importance: "MANDATORY" - standards: - ["CC 2.1", "CC 6.1", "CC 6.6", "CC 6.8", "CC 7.1", "CC 7.2", "CC 7.3"] - description: | - It allows you to detect and respond to potential security incidents or system failures immediately, minimizing downtime and reducing the risk of data breaches. It maintain visibility into system performance and security events. - - content-ref: "99b96e82-f750-4bca-85cc-860dcc681298" - category: "Log collection and monitoring" - name: "Keep your logs" - importance: "MANDATORY" - standards: ["CC 7.3"] - description: | - Logs help you to detect and investigate security incidents and troubleshoot operational issues. They provide an audit trail. - - content-ref: "0c384a4d-da22-4a90-ba0f-a83812a17f49" - category: "Log collection and monitoring" - name: "Be automatically notified of issues" - importance: "MANDATORY" - standards: ["6.1", "6.6", "6.8", "CC 7.1", "CC 7.2", "CC 7.3"] - description: | - Setting up alerting allows you to respond quickly and minimize downtime or potential data breaches. It ensures that important events, such as unauthorized access or system failures, are addressed promptly. - - content-ref: "c3fb8a93-4be5-44da-bcbc-8ee01cb31d2e" - category: "Prepare for incidents" - name: "Build an incident response process" - importance: "MANDATORY" - standards: ["CC 2.2"] - description: | - An incident response plan is crucial for quickly identifying, containing, and resolving incidents, minimizing potential disruptions. It ensures you’re prepared effectively and that you keep your operations running smoothly, even in the face of unexpected threats. - - content-ref: "07659ea9-7fce-4557-932e-49384b01bc10" - category: "Prepare for incidents" - name: "Create & test your disaster recovery plan" - importance: "MANDATORY" - standards: ["CC 3.2", "CC 5.1", "CC 7.5", "CC 8.1"] - description: | - Disasters might seem far-fetched, but data loss, service outages, and misconfigurations can happen. Whether it's a simple human error or a regional outage, a DRP helps your team recover fast and maintain customer trust. - - content-ref: "3745b7c3-d5ac-45da-80a5-3cf2009bd946" - category: "Share your SOC 2" - name: "Have a security page" - importance: "MANDATORY" - standards: ["CC 2.3"] - description: | - Having SOC-2 for yourself is useless, you want to leverage it as much as possible. - - content-ref: "be4808c3-8b1e-4d0a-a936-b067716d88e1" - category: "Share your SOC 2" - name: "Clearly explain your services" - importance: "MANDATORY" - standards: ["CC 2.3"] - description: | - Here, we are not enhancing security, we are helping the auditor be more efficient. As part of the audit, he/she will need to understand clear boundaries for the audit, identify the relevant risk and then check how you mitigate them. - - A proper service description is key for your customers, but it also help the auditor understands what your customers are expecting from you. - - content-ref: "83a018c0-1031-4637-b9ca-7210330be626" - category: "Share your SOC 2" - name: "External support available" - importance: "PREFERRED" - standards: ["CC 2.3"] - description: | - External support is not required for SOC 2, but it enhances transparency by showcasing your security practices, policies, and compliance efforts, building trust with customers and stakeholders. - Ultimately, this is what SOC 2 is about: reassuring your prospect and customer on how you work. diff --git a/pkg/coredata/control.go b/pkg/coredata/control.go new file mode 100644 index 000000000..8d5a0a258 --- /dev/null +++ b/pkg/coredata/control.go @@ -0,0 +1,262 @@ +// Copyright (c) 2025 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +import ( + "context" + "fmt" + "maps" + "time" + + "github.com/getprobo/probo/pkg/gid" + "github.com/getprobo/probo/pkg/page" + "github.com/jackc/pgx/v5" + "go.gearno.de/kit/pg" +) + +type ( + Control struct { + ID gid.GID `db:"id"` + ReferenceID string `db:"reference_id"` + FrameworkID gid.GID `db:"framework_id"` + TenantID gid.TenantID `db:"tenant_id"` + Name string `db:"name"` + Description string `db:"description"` + CreatedAt time.Time `db:"created_at"` + UpdatedAt time.Time `db:"updated_at"` + } + + Controls []*Control + + UpdateControlParams struct { + ExpectedVersion int + Name *string + Description *string + } +) + +func (c Control) CursorKey(orderBy ControlOrderField) page.CursorKey { + switch orderBy { + case ControlOrderFieldCreatedAt: + return page.CursorKey{ID: c.ID, Value: c.CreatedAt} + } + + panic(fmt.Sprintf("unsupported order by: %s", orderBy)) +} + +func (c *Controls) LoadByFrameworkID( + ctx context.Context, + conn pg.Conn, + scope Scoper, + frameworkID gid.GID, + cursor *page.Cursor[ControlOrderField], +) error { + q := ` +SELECT + id, + reference_id, + framework_id, + tenant_id, + name, + description, + created_at, + updated_at +FROM + controls +WHERE + %s + AND framework_id = @framework_id + AND %s +` + q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment()) + + args := pgx.NamedArgs{"framework_id": frameworkID} + maps.Copy(args, scope.SQLArguments()) + + rows, err := conn.Query(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot query controls: %w", err) + } + + controls, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Control]) + if err != nil { + return fmt.Errorf("cannot collect controls: %w", err) + } + + *c = controls + + return nil +} + +func (c *Control) LoadByID( + ctx context.Context, + conn pg.Conn, + scope Scoper, + controlID gid.GID, +) error { + q := ` +SELECT + id, + reference_id, + framework_id, + tenant_id, + name, + description, + created_at, + updated_at +FROM + controls +WHERE + %s + AND id = @control_id +LIMIT 1; +` + q = fmt.Sprintf(q, scope.SQLFragment()) + + args := pgx.StrictNamedArgs{"control_id": controlID} + maps.Copy(args, scope.SQLArguments()) + rows, err := conn.Query(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot query controls: %w", err) + } + + control, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[Control]) + if err != nil { + return fmt.Errorf("cannot collect control: %w", err) + } + + *c = control + + return nil +} + +func (c Control) Insert( + ctx context.Context, + conn pg.Conn, + scope Scoper, +) error { + q := ` +INSERT INTO + controls ( + tenant_id, + id, + framework_id, + reference_id, + name, + description, + created_at, + updated_at + ) +VALUES ( + @tenant_id, + @control_id, + @framework_id, + @reference_id, + @name, + @description, + @created_at, + @updated_at +); +` + + args := pgx.StrictNamedArgs{ + "tenant_id": scope.GetTenantID(), + "control_id": c.ID, + "framework_id": c.FrameworkID, + "reference_id": c.ReferenceID, + "name": c.Name, + "description": c.Description, + "created_at": c.CreatedAt, + "updated_at": c.UpdatedAt, + } + _, err := conn.Exec(ctx, q, args) + return err +} + +func (c Control) Delete( + ctx context.Context, + conn pg.Conn, + scope Scoper, +) error { + q := ` +DELETE +FROM + controls +WHERE + %s + AND id = @control_id; +` + + args := pgx.StrictNamedArgs{"control_id": c.ID} + maps.Copy(args, scope.SQLArguments()) + q = fmt.Sprintf(q, scope.SQLFragment()) + + _, err := conn.Exec(ctx, q, args) + return err +} + +func (c *Control) Update( + ctx context.Context, + conn pg.Conn, + scope Scoper, + params UpdateControlParams, +) error { + q := ` +UPDATE controls SET + name = COALESCE(@name, name), + description = COALESCE(@description, description), + updated_at = @updated_at +WHERE %s + AND id = @control_id +RETURNING + id, + framework_id, + tenant_id, + name, + description, + created_at, + updated_at +` + q = fmt.Sprintf(q, scope.SQLFragment()) + + args := pgx.StrictNamedArgs{ + "control_id": c.ID, + "expected_version": params.ExpectedVersion, + "updated_at": time.Now(), + } + + if params.Name != nil { + args["name"] = *params.Name + } + if params.Description != nil { + args["description"] = *params.Description + } + + maps.Copy(args, scope.SQLArguments()) + + rows, err := conn.Query(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot query controls: %w", err) + } + + control, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[Control]) + if err != nil { + return fmt.Errorf("cannot collect control: %w", err) + } + + *c = control + + return nil +} diff --git a/pkg/coredata/control_mitigation.go b/pkg/coredata/control_mitigation.go new file mode 100644 index 000000000..92145b96b --- /dev/null +++ b/pkg/coredata/control_mitigation.go @@ -0,0 +1,168 @@ +// Copyright (c) 2025 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +import ( + "context" + "fmt" + "maps" + "time" + + "github.com/getprobo/probo/pkg/gid" + "github.com/jackc/pgx/v5" + "go.gearno.de/kit/pg" +) + +type ( + ControlMitigation struct { + ControlID gid.GID `db:"control_id"` + MitigationID gid.GID `db:"mitigation_id"` + TenantID gid.TenantID `db:"tenant_id"` + CreatedAt time.Time `db:"created_at"` + } + + ControlMitigations []*ControlMitigation +) + +func (cm ControlMitigation) Insert( + ctx context.Context, + conn pg.Conn, + scope Scoper, +) error { + q := ` +INSERT INTO + control_mitigations ( + control_id, + mitigation_id, + tenant_id, + created_at + ) +VALUES ( + @control_id, + @mitigation_id, + @tenant_id, + @created_at +); +` + + args := pgx.StrictNamedArgs{ + "control_id": cm.ControlID, + "mitigation_id": cm.MitigationID, + "tenant_id": scope.GetTenantID(), + "created_at": cm.CreatedAt, + } + _, err := conn.Exec(ctx, q, args) + return err +} + +func (cm ControlMitigation) Delete( + ctx context.Context, + conn pg.Conn, + scope Scoper, +) error { + q := ` +DELETE +FROM + control_mitigations +WHERE + %s + AND control_id = @control_id + AND mitigation_id = @mitigation_id; +` + + args := pgx.StrictNamedArgs{ + "control_id": cm.ControlID, + "mitigation_id": cm.MitigationID, + } + maps.Copy(args, scope.SQLArguments()) + q = fmt.Sprintf(q, scope.SQLFragment()) + + _, err := conn.Exec(ctx, q, args) + return err +} + +func (cms *ControlMitigations) LoadByMitigationID( + ctx context.Context, + conn pg.Conn, + scope Scoper, + mitigationID gid.GID, +) error { + q := ` +SELECT + control_id, + mitigation_id, + tenant_id, + created_at +FROM + control_mitigations +WHERE + %s + AND mitigation_id = @mitigation_id +` + q = fmt.Sprintf(q, scope.SQLFragment()) + + args := pgx.StrictNamedArgs{"mitigation_id": mitigationID} + maps.Copy(args, scope.SQLArguments()) + + rows, err := conn.Query(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot query control_mitigations: %w", err) + } + + controlMitigations, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ControlMitigation]) + if err != nil { + return fmt.Errorf("cannot collect control_mitigations: %w", err) + } + + *cms = controlMitigations + return nil +} + +func (cms *ControlMitigations) LoadByControlID( + ctx context.Context, + conn pg.Conn, + scope Scoper, + controlID gid.GID, +) error { + q := ` +SELECT + control_id, + mitigation_id, + tenant_id, + created_at +FROM + control_mitigations +WHERE + %s + AND control_id = @control_id +` + q = fmt.Sprintf(q, scope.SQLFragment()) + + args := pgx.StrictNamedArgs{"control_id": controlID} + maps.Copy(args, scope.SQLArguments()) + + rows, err := conn.Query(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot query control_mitigations: %w", err) + } + + controlMitigations, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ControlMitigation]) + if err != nil { + return fmt.Errorf("cannot collect control_mitigations: %w", err) + } + + *cms = controlMitigations + return nil +} diff --git a/pkg/coredata/control_order_field.go b/pkg/coredata/control_order_field.go new file mode 100644 index 000000000..8004efe45 --- /dev/null +++ b/pkg/coredata/control_order_field.go @@ -0,0 +1,40 @@ +// Copyright (c) 2025 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +type ( + ControlOrderField string +) + +const ( + ControlOrderFieldCreatedAt ControlOrderField = "CREATED_AT" +) + +func (p ControlOrderField) Column() string { + return string(p) +} + +func (p ControlOrderField) String() string { + return string(p) +} + +func (p ControlOrderField) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *ControlOrderField) UnmarshalText(text []byte) error { + *p = ControlOrderField(text) + return nil +} diff --git a/pkg/coredata/entity_type_reg.go b/pkg/coredata/entity_type_reg.go index fb4c14578..225814468 100644 --- a/pkg/coredata/entity_type_reg.go +++ b/pkg/coredata/entity_type_reg.go @@ -29,4 +29,5 @@ const ( UserEntityType SessionEntityType EmailEntityType + ControlEntityType ) diff --git a/pkg/coredata/framework.go b/pkg/coredata/framework.go index fa6e570d6..8ae7bef1c 100644 --- a/pkg/coredata/framework.go +++ b/pkg/coredata/framework.go @@ -30,27 +30,20 @@ type ( Framework struct { ID gid.GID `db:"id"` OrganizationID gid.GID `db:"organization_id"` + ReferenceID string `db:"reference_id"` Name string `db:"name"` Description string `db:"description"` - ContentRef string `db:"content_ref"` CreatedAt time.Time `db:"created_at"` UpdatedAt time.Time `db:"updated_at"` - Version int `db:"version"` } Frameworks []*Framework - - UpdateFrameworkParams struct { - ExpectedVersion int - Name *string - Description *string - } ) -func (f Framework) CursorKey(orderBy FrameworkOrderField) page.CursorKey { +func (f *Framework) CursorKey(orderBy FrameworkOrderField) page.CursorKey { switch orderBy { case FrameworkOrderFieldCreatedAt: - return page.NewCursorKey(f.ID, f.CreatedAt) + return page.CursorKey{ID: f.ID, Value: f.CreatedAt} } panic(fmt.Sprintf("unsupported order by: %s", orderBy)) @@ -67,25 +60,23 @@ func (f *Frameworks) LoadByOrganizationID( SELECT id, organization_id, + reference_id, name, description, - content_ref, created_at, - updated_at, - version + updated_at FROM frameworks WHERE %s AND organization_id = @organization_id - AND %s + AND %s ` q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment()) args := pgx.NamedArgs{"organization_id": organizationID} maps.Copy(args, scope.SQLArguments()) - maps.Copy(args, cursor.SQLArguments()) rows, err := conn.Query(ctx, q, args) if err != nil { @@ -112,12 +103,11 @@ func (f *Framework) LoadByID( SELECT id, organization_id, + reference_id, name, description, - content_ref, created_at, - updated_at, - version + updated_at FROM frameworks WHERE @@ -156,23 +146,21 @@ INSERT INTO tenant_id, id, organization_id, + reference_id, name, description, - content_ref, created_at, - updated_at, - version + updated_at ) VALUES ( @tenant_id, @framework_id, @organization_id, + @reference_id, @name, @description, - @content_ref, @created_at, - @updated_at, - @version + @updated_at ); ` @@ -180,12 +168,11 @@ VALUES ( "tenant_id": scope.GetTenantID(), "framework_id": f.ID, "organization_id": f.OrganizationID, + "reference_id": f.ReferenceID, "name": f.Name, "description": f.Description, - "content_ref": f.ContentRef, "created_at": f.CreatedAt, "updated_at": f.UpdatedAt, - "version": f.Version, } _, err := conn.Exec(ctx, q, args) return err @@ -217,55 +204,28 @@ func (f *Framework) Update( ctx context.Context, conn pg.Conn, scope Scoper, - params UpdateFrameworkParams, ) error { q := ` -UPDATE frameworks SET - name = COALESCE(@name, name), - description = COALESCE(@description, description), - updated_at = @updated_at, - version = version + 1 -WHERE %s - AND id = @framework_id - AND version = @expected_version -RETURNING - id, - organization_id, - name, - description, - content_ref, - created_at, - updated_at, - version +UPDATE frameworks +SET + name = @name, + description = @description, + updated_at = @updated_at +WHERE + %s + AND id = @framework_id ` q = fmt.Sprintf(q, scope.SQLFragment()) args := pgx.StrictNamedArgs{ - "framework_id": f.ID, - "expected_version": params.ExpectedVersion, - "updated_at": time.Now(), - } - - if params.Name != nil { - args["name"] = *params.Name - } - if params.Description != nil { - args["description"] = *params.Description + "framework_id": f.ID, + "updated_at": f.UpdatedAt, + "name": f.Name, + "description": f.Description, } maps.Copy(args, scope.SQLArguments()) - rows, err := conn.Query(ctx, q, args) - if err != nil { - return fmt.Errorf("cannot query frameworks: %w", err) - } - - framework, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[Framework]) - if err != nil { - return fmt.Errorf("cannot collect framework: %w", err) - } - - *f = framework - - return nil + _, err := conn.Exec(ctx, q, args) + return err } diff --git a/pkg/coredata/migrations/20250327T122105Z.sql b/pkg/coredata/migrations/20250327T122105Z.sql new file mode 100644 index 000000000..6ab06f6b9 --- /dev/null +++ b/pkg/coredata/migrations/20250327T122105Z.sql @@ -0,0 +1,18 @@ +-- Add organization_id column to mitigations table +ALTER TABLE mitigations ADD COLUMN organization_id TEXT; + +-- Update mitigations to set organization_id based on framework's organization_id +UPDATE mitigations m +SET organization_id = f.organization_id +FROM frameworks f +WHERE m.framework_id = f.id; + +-- Make organization_id NOT NULL after update +ALTER TABLE mitigations ALTER COLUMN organization_id SET NOT NULL; + +-- Add foreign key constraint +ALTER TABLE mitigations ADD CONSTRAINT fk_mitigations_organization_id + FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE; + +-- Add index for performance +CREATE INDEX idx_mitigations_organization_id ON mitigations(organization_id); \ No newline at end of file diff --git a/pkg/coredata/migrations/20250327T122609Z.sql b/pkg/coredata/migrations/20250327T122609Z.sql new file mode 100644 index 000000000..983e16e5e --- /dev/null +++ b/pkg/coredata/migrations/20250327T122609Z.sql @@ -0,0 +1,8 @@ +-- Drop the foreign key constraint first +ALTER TABLE mitigations DROP CONSTRAINT IF EXISTS fk_mitigations_framework_id; + +-- Drop any indices on framework_id +DROP INDEX IF EXISTS idx_mitigations_framework_id; + +-- Remove the framework_id column +ALTER TABLE mitigations DROP COLUMN framework_id; \ No newline at end of file diff --git a/pkg/coredata/migrations/20250327T210900Z.sql b/pkg/coredata/migrations/20250327T210900Z.sql new file mode 100644 index 000000000..ee76e077d --- /dev/null +++ b/pkg/coredata/migrations/20250327T210900Z.sql @@ -0,0 +1 @@ +ALTER TABLE frameworks ADD COLUMN reference_id TEXT; \ No newline at end of file diff --git a/pkg/coredata/migrations/20250327T212900Z.sql b/pkg/coredata/migrations/20250327T212900Z.sql new file mode 100644 index 000000000..f52a25c28 --- /dev/null +++ b/pkg/coredata/migrations/20250327T212900Z.sql @@ -0,0 +1,2 @@ +ALTER TABLE controls ADD COLUMN reference_id TEXT NOT NULL DEFAULT ''; +ALTER TABLE controls ALTER COLUMN reference_id DROP DEFAULT; \ No newline at end of file diff --git a/pkg/coredata/migrations/20250327T220600Z.sql b/pkg/coredata/migrations/20250327T220600Z.sql new file mode 100644 index 000000000..33f641a7a --- /dev/null +++ b/pkg/coredata/migrations/20250327T220600Z.sql @@ -0,0 +1 @@ +ALTER TABLE frameworks DROP COLUMN content_ref; \ No newline at end of file diff --git a/pkg/coredata/migrations/20250327T220601Z.sql b/pkg/coredata/migrations/20250327T220601Z.sql new file mode 100644 index 000000000..7c8258706 --- /dev/null +++ b/pkg/coredata/migrations/20250327T220601Z.sql @@ -0,0 +1 @@ +ALTER TABLE frameworks DROP COLUMN version; \ No newline at end of file diff --git a/pkg/coredata/migrations/20250607T120000Z.sql b/pkg/coredata/migrations/20250607T120000Z.sql new file mode 100644 index 000000000..e85a2314f --- /dev/null +++ b/pkg/coredata/migrations/20250607T120000Z.sql @@ -0,0 +1,19 @@ +-- Create a new table for controls with string IDs +CREATE TABLE controls ( + id TEXT PRIMARY KEY, + tenant_id TEXT NOT NULL, + framework_id TEXT NOT NULL, + name TEXT NOT NULL, + description TEXT NOT NULL, + created_at TIMESTAMP WITH TIME ZONE NOT NULL, + updated_at TIMESTAMP WITH TIME ZONE NOT NULL, + version INTEGER NOT NULL DEFAULT 0 +); + +CREATE TABLE controls_mitigations ( + control_id TEXT NOT NULL, + mitigation_id TEXT NOT NULL, + tenant_id TEXT NOT NULL, + created_at TIMESTAMP WITH TIME ZONE NOT NULL, + PRIMARY KEY (control_id, mitigation_id) +); diff --git a/pkg/coredata/mitigation.go b/pkg/coredata/mitigation.go index c7b32fb33..aceb8c61d 100644 --- a/pkg/coredata/mitigation.go +++ b/pkg/coredata/mitigation.go @@ -29,18 +29,18 @@ import ( type ( Mitigation struct { - ID gid.GID `db:"id"` - FrameworkID gid.GID `db:"framework_id"` - Category string `db:"category"` - Name string `db:"name"` - Description string `db:"description"` - Importance MitigationImportance `db:"importance"` - State MitigationState `db:"state"` - ContentRef string `db:"content_ref"` - CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"updated_at"` - Version int `db:"version"` - Standards []string `db:"standards"` + ID gid.GID `db:"id"` + OrganizationID gid.GID `db:"organization_id"` + Category string `db:"category"` + Name string `db:"name"` + Description string `db:"description"` + Importance MitigationImportance `db:"importance"` + State MitigationState `db:"state"` + ContentRef string `db:"content_ref"` + CreatedAt time.Time `db:"created_at"` + UpdatedAt time.Time `db:"updated_at"` + Version int `db:"version"` + Standards []string `db:"standards"` } Mitigations []*Mitigation @@ -73,7 +73,7 @@ func (c *Mitigation) LoadByID( q := ` SELECT id, - framework_id, + organization_id, category, name, description, @@ -122,7 +122,7 @@ INSERT INTO mitigations ( tenant_id, id, - framework_id, + organization_id, category, name, importance, @@ -137,7 +137,7 @@ INSERT INTO VALUES ( @tenant_id, @mitigation_id, - @framework_id, + @organization_id, @category, @name, @importance, @@ -152,35 +152,35 @@ VALUES ( ` args := pgx.StrictNamedArgs{ - "tenant_id": scope.GetTenantID(), - "mitigation_id": c.ID, - "framework_id": c.FrameworkID, - "category": c.Category, - "name": c.Name, - "version": 0, - "description": c.Description, - "content_ref": c.ContentRef, - "created_at": c.CreatedAt, - "updated_at": c.UpdatedAt, - "state": c.State, - "importance": c.Importance, - "standards": c.Standards, + "tenant_id": scope.GetTenantID(), + "mitigation_id": c.ID, + "organization_id": c.OrganizationID, + "category": c.Category, + "name": c.Name, + "version": 0, + "description": c.Description, + "content_ref": c.ContentRef, + "created_at": c.CreatedAt, + "updated_at": c.UpdatedAt, + "state": c.State, + "importance": c.Importance, + "standards": c.Standards, } _, err := conn.Exec(ctx, q, args) return err } -func (c *Mitigations) LoadByFrameworkID( +func (c *Mitigations) LoadByOrganizationID( ctx context.Context, conn pg.Conn, scope Scoper, - frameworkID gid.GID, + organizationID gid.GID, cursor *page.Cursor[MitigationOrderField], ) error { q := ` SELECT id, - framework_id, + organization_id, category, name, description, @@ -195,12 +195,12 @@ FROM mitigations WHERE %s - AND framework_id = @framework_id + AND organization_id = @organization_id AND %s ` q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment()) - args := pgx.StrictNamedArgs{"framework_id": frameworkID} + args := pgx.StrictNamedArgs{"organization_id": organizationID} maps.Copy(args, scope.SQLArguments()) maps.Copy(args, cursor.SQLArguments()) @@ -239,7 +239,7 @@ WHERE %s AND version = @expected_version RETURNING id, - framework_id, + organization_id, category, name, description, @@ -248,8 +248,8 @@ RETURNING content_ref, created_at, updated_at, - version, - standards + standards, + version ` q = fmt.Sprintf(q, scope.SQLFragment()) diff --git a/pkg/page/cursor_key.go b/pkg/page/cursor_key.go index 978282e66..7b338bf6f 100644 --- a/pkg/page/cursor_key.go +++ b/pkg/page/cursor_key.go @@ -27,6 +27,12 @@ type CursorKey struct { Value any } +// StringCursorKey is a cursor key for string IDs +type StringCursorKey struct { + ID string + Value any +} + var ( CursorKeyNil CursorKey diff --git a/pkg/probo/control_service.go b/pkg/probo/control_service.go new file mode 100644 index 000000000..7e6b0daf2 --- /dev/null +++ b/pkg/probo/control_service.go @@ -0,0 +1,282 @@ +// Copyright (c) 2025 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package probo + +import ( + "context" + "fmt" + "time" + + "github.com/getprobo/probo/pkg/coredata" + "github.com/getprobo/probo/pkg/gid" + "github.com/getprobo/probo/pkg/page" + "go.gearno.de/kit/pg" +) + +type ( + ControlService struct { + svc *TenantService + } + + CreateControlRequest struct { + ID gid.GID + FrameworkID gid.GID + Name string + Description string + } + + UpdateControlRequest struct { + ID gid.GID + ExpectedVersion int + Name *string + Description *string + } + + ConnectControlToMitigationRequest struct { + ControlID gid.GID + MitigationID gid.GID + } + + DisconnectControlFromMitigationRequest struct { + ControlID gid.GID + MitigationID gid.GID + } +) + +// Create creates a new control +func (s ControlService) Create( + ctx context.Context, + req CreateControlRequest, +) (*coredata.Control, error) { + now := time.Now() + + control := &coredata.Control{ + ID: req.ID, + FrameworkID: req.FrameworkID, + TenantID: s.svc.scope.GetTenantID(), + Name: req.Name, + Description: req.Description, + CreatedAt: now, + UpdatedAt: now, + } + + err := s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return control.Insert(ctx, conn, s.svc.scope) + }, + ) + + if err != nil { + return nil, fmt.Errorf("cannot create control: %w", err) + } + + return control, nil +} + +// Get retrieves a control by ID +func (s ControlService) Get( + ctx context.Context, + controlID gid.GID, +) (*coredata.Control, error) { + control := &coredata.Control{} + + err := s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return control.LoadByID(ctx, conn, s.svc.scope, controlID) + }, + ) + + if err != nil { + return nil, fmt.Errorf("cannot get control: %w", err) + } + + return control, nil +} + +// Update updates an existing control +func (s ControlService) Update( + ctx context.Context, + req UpdateControlRequest, +) (*coredata.Control, error) { + params := coredata.UpdateControlParams{ + ExpectedVersion: req.ExpectedVersion, + Name: req.Name, + Description: req.Description, + } + + control := &coredata.Control{ID: req.ID} + + err := s.svc.pg.WithTx( + ctx, + func(conn pg.Conn) error { + return control.Update(ctx, conn, s.svc.scope, params) + }) + if err != nil { + return nil, fmt.Errorf("cannot update control: %w", err) + } + + return control, nil +} + +// Delete removes a control +func (s ControlService) Delete( + ctx context.Context, + controlID gid.GID, +) error { + control := &coredata.Control{ID: controlID} + + return s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return control.Delete(ctx, conn, s.svc.scope) + }, + ) +} + +// ListForFrameworkID retrieves all controls for a framework +func (s ControlService) ListForFrameworkID( + ctx context.Context, + frameworkID gid.GID, + cursor *page.Cursor[coredata.ControlOrderField], +) (*page.Page[*coredata.Control, coredata.ControlOrderField], error) { + var controls coredata.Controls + + err := s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return controls.LoadByFrameworkID( + ctx, + conn, + s.svc.scope, + frameworkID, + cursor, + ) + }, + ) + + if err != nil { + return nil, fmt.Errorf("cannot list controls: %w", err) + } + + return page.NewPage(controls, cursor), nil +} + +func (s ControlService) ConnectToMitigation( + ctx context.Context, + req ConnectControlToMitigationRequest, +) error { + now := time.Now() + + controlMitigation := &coredata.ControlMitigation{ + ControlID: req.ControlID, + MitigationID: req.MitigationID, + TenantID: s.svc.scope.GetTenantID(), + CreatedAt: now, + } + + return s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return controlMitigation.Insert(ctx, conn, s.svc.scope) + }, + ) +} + +// DisconnectFromMitigation removes the link between a control and a mitigation +func (s ControlService) DisconnectFromMitigation( + ctx context.Context, + req DisconnectControlFromMitigationRequest, +) error { + controlMitigation := &coredata.ControlMitigation{ + ControlID: req.ControlID, + MitigationID: req.MitigationID, + } + + return s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return controlMitigation.Delete(ctx, conn, s.svc.scope) + }, + ) +} + +func (s ControlService) ListMitigationsForControlID( + ctx context.Context, + controlID gid.GID, +) ([]*coredata.Mitigation, error) { + var controlMitigations coredata.ControlMitigations + var mitigations []*coredata.Mitigation + + err := s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + if err := controlMitigations.LoadByControlID(ctx, conn, s.svc.scope, controlID); err != nil { + return fmt.Errorf("cannot load control mitigations: %w", err) + } + + for _, cm := range controlMitigations { + mitigation := &coredata.Mitigation{} + if err := mitigation.LoadByID(ctx, conn, s.svc.scope, cm.MitigationID); err != nil { + return fmt.Errorf("cannot load mitigation: %w", err) + } + mitigations = append(mitigations, mitigation) + } + + return nil + }, + ) + + if err != nil { + return nil, err + } + + return mitigations, nil +} + +// ListControlsForMitigationID retrieves all controls linked to a mitigation +func (s ControlService) ListControlsForMitigationID( + ctx context.Context, + mitigationID gid.GID, +) ([]*coredata.Control, error) { + var controlMitigations coredata.ControlMitigations + var controls []*coredata.Control + + err := s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + if err := controlMitigations.LoadByMitigationID(ctx, conn, s.svc.scope, mitigationID); err != nil { + return fmt.Errorf("cannot load control mitigations: %w", err) + } + + for _, cm := range controlMitigations { + control := &coredata.Control{} + if err := control.LoadByID(ctx, conn, s.svc.scope, cm.ControlID); err != nil { + return fmt.Errorf("cannot load control: %w", err) + } + controls = append(controls, control) + } + + return nil + }, + ) + + if err != nil { + return nil, err + } + + return controls, nil +} diff --git a/pkg/probo/framework_service.go b/pkg/probo/framework_service.go index 66db80c7d..c687d62ae 100644 --- a/pkg/probo/framework_service.go +++ b/pkg/probo/framework_service.go @@ -19,7 +19,6 @@ import ( "fmt" "time" - "gearno.de/ref" "github.com/getprobo/probo/pkg/coredata" "github.com/getprobo/probo/pkg/gid" "github.com/getprobo/probo/pkg/page" @@ -34,38 +33,22 @@ type ( CreateFrameworkRequest struct { OrganizationID gid.GID Name string - Description string - ContentRef string } UpdateFrameworkRequest struct { - ID gid.GID - ExpectedVersion int - Name *string - Description *string + ID gid.GID + Name *string + Description *string } ImportFrameworkRequest struct { - Data struct { - Framework struct { + Framework struct { + Name string `json:"name"` + Controls []struct { + ID string `json:"id"` Name string `json:"name"` - ContentRef string `json:"content-ref"` Description string `json:"description"` - Version string `json:"version"` - Controls []struct { - ContentRef string `json:"content-ref"` - Category string `json:"category"` - Importance coredata.MitigationImportance `json:"importance"` - Standards []string `json:"standards"` - Name string `json:"name"` - Description string `json:"description"` - Tasks []struct { - Name string `json:"name"` - Description string `json:"description"` - TimeEstimate int `json:"time-estimate"` - } `json:"tasks"` - } `json:"controls"` - } `json:"framework"` + } `json:"controls"` } } ) @@ -84,8 +67,6 @@ func (s FrameworkService) Create( ID: frameworkID, OrganizationID: req.OrganizationID, Name: req.Name, - Description: req.Description, - ContentRef: req.ContentRef, CreatedAt: now, UpdatedAt: now, } @@ -155,19 +136,26 @@ func (s FrameworkService) Update( ctx context.Context, req UpdateFrameworkRequest, ) (*coredata.Framework, error) { - params := coredata.UpdateFrameworkParams{ - ExpectedVersion: req.ExpectedVersion, - Name: req.Name, - Description: req.Description, - } - framework := &coredata.Framework{ID: req.ID} err := s.svc.pg.WithTx( ctx, func(conn pg.Conn) error { - return framework.Update(ctx, conn, s.svc.scope, params) - }) + if err := framework.LoadByID(ctx, conn, s.svc.scope, req.ID); err != nil { + return fmt.Errorf("cannot load framework: %w", err) + } + + if req.Name != nil { + framework.Name = *req.Name + } + + if req.Description != nil { + framework.Description = *req.Description + } + + return framework.Update(ctx, conn, s.svc.scope) + }, + ) if err != nil { return nil, err } @@ -194,70 +182,41 @@ func (s FrameworkService) Import( organizationID gid.GID, req ImportFrameworkRequest, ) (*coredata.Framework, error) { - - now := time.Now() - frameworkID, err := gid.NewGID(organizationID.TenantID(), coredata.FrameworkEntityType) if err != nil { return nil, fmt.Errorf("cannot create global id: %w", err) } + now := time.Now() framework := &coredata.Framework{ ID: frameworkID, OrganizationID: organizationID, - Name: req.Data.Framework.Name, - Description: req.Data.Framework.Description, - ContentRef: req.Data.Framework.ContentRef, + ReferenceID: req.Framework.Name, + Name: req.Framework.Name, CreatedAt: now, UpdatedAt: now, } - importedMitigations := coredata.Mitigations{} - importedTasks := coredata.Tasks{} - for _, mitigation := range req.Data.Framework.Controls { - controlID, err := gid.NewGID(organizationID.TenantID(), coredata.MitigationEntityType) + importedControls := coredata.Controls{} + for _, control := range req.Framework.Controls { + controlID, err := gid.NewGID(organizationID.TenantID(), coredata.ControlEntityType) if err != nil { return nil, fmt.Errorf("cannot create global id: %w", err) } - importedControl := &coredata.Mitigation{ + now := time.Now() + control := &coredata.Control{ ID: controlID, + TenantID: organizationID.TenantID(), FrameworkID: frameworkID, - Category: mitigation.Category, - Importance: coredata.MitigationImportance(mitigation.Importance), - Name: mitigation.Name, - Description: mitigation.Description, - State: coredata.MitigationStateNotStarted, - ContentRef: mitigation.ContentRef, + ReferenceID: control.ID, + Name: control.Name, + Description: control.Description, CreatedAt: now, UpdatedAt: now, - Standards: mitigation.Standards, } - importedMitigations = append(importedMitigations, importedControl) - - for _, task := range mitigation.Tasks { - taskID, err := gid.NewGID(organizationID.TenantID(), coredata.TaskEntityType) - if err != nil { - return nil, fmt.Errorf("cannot create global id: %w", err) - } - - var timeEstimate *time.Duration - if task.TimeEstimate > 0 { - timeEstimate = ref.Ref(time.Duration(task.TimeEstimate) * time.Second) - } - - importedTasks = append(importedTasks, &coredata.Task{ - ID: taskID, - MitigationID: controlID, - Name: task.Name, - State: coredata.TaskStateTodo, - Description: task.Description, - CreatedAt: now, - UpdatedAt: now, - TimeEstimate: timeEstimate, - }) - } + importedControls = append(importedControls, control) } err = s.svc.pg.WithTx( @@ -269,15 +228,9 @@ func (s FrameworkService) Import( return fmt.Errorf("cannot insert framework: %w", err) } - for _, importedMitigation := range importedMitigations { - if err := importedMitigation.Insert(ctx, tx, s.svc.scope); err != nil { - return fmt.Errorf("cannot insert mitigation: %w", err) - } - } - - for _, importedTask := range importedTasks { - if err := importedTask.Insert(ctx, tx, s.svc.scope); err != nil { - return fmt.Errorf("cannot insert task: %w", err) + for _, importedControl := range importedControls { + if err := importedControl.Insert(ctx, tx, s.svc.scope); err != nil { + return fmt.Errorf("cannot insert control: %w", err) } } diff --git a/pkg/probo/mitigation_service.go b/pkg/probo/mitigation_service.go index 5820e64fc..d8f77e865 100644 --- a/pkg/probo/mitigation_service.go +++ b/pkg/probo/mitigation_service.go @@ -31,11 +31,11 @@ type ( } CreateMitigationRequest struct { - FrameworkID gid.GID - Name string - Description string - Category string - Importance coredata.MitigationImportance + OrganizationID gid.GID + Name string + Description string + Category string + Importance coredata.MitigationImportance } UpdateMitigationRequest struct { @@ -96,9 +96,9 @@ func (s MitigationService) Update( return mitigation, nil } -func (s MitigationService) ListForFrameworkID( +func (s MitigationService) ListForOrganizationID( ctx context.Context, - frameworkID gid.GID, + organizationID gid.GID, cursor *page.Cursor[coredata.MitigationOrderField], ) (*page.Page[*coredata.Mitigation, coredata.MitigationOrderField], error) { var mitigations coredata.Mitigations @@ -106,11 +106,11 @@ func (s MitigationService) ListForFrameworkID( err := s.svc.pg.WithConn( ctx, func(conn pg.Conn) error { - return mitigations.LoadByFrameworkID( + return mitigations.LoadByOrganizationID( ctx, conn, s.svc.scope, - frameworkID, + organizationID, cursor, ) }, @@ -133,27 +133,22 @@ func (s MitigationService) Create( return nil, fmt.Errorf("cannot create mitigation global id: %w", err) } - framework := &coredata.Framework{} mitigation := &coredata.Mitigation{ - ID: mitigationID, - FrameworkID: req.FrameworkID, - Name: req.Name, - Description: req.Description, - Category: req.Category, - State: coredata.MitigationStateNotStarted, - Standards: []string{}, - Importance: req.Importance, - CreatedAt: now, - UpdatedAt: now, + ID: mitigationID, + OrganizationID: req.OrganizationID, + Name: req.Name, + Description: req.Description, + Category: req.Category, + State: coredata.MitigationStateNotStarted, + Standards: []string{}, + Importance: req.Importance, + CreatedAt: now, + UpdatedAt: now, } err = s.svc.pg.WithTx( ctx, func(conn pg.Conn) error { - if err := framework.LoadByID(ctx, conn, s.svc.scope, req.FrameworkID); err != nil { - return fmt.Errorf("cannot load framework %q: %w", req.FrameworkID, err) - } - if err := mitigation.Insert(ctx, conn, s.svc.scope); err != nil { return fmt.Errorf("cannot insert mitigation: %w", err) } diff --git a/pkg/probo/service.go b/pkg/probo/service.go index cf6e1d6bd..8e5491fa2 100644 --- a/pkg/probo/service.go +++ b/pkg/probo/service.go @@ -32,18 +32,21 @@ type ( } TenantService struct { - pg *pg.Client - s3 *s3.Client - bucket string - scope coredata.Scoper + pg *pg.Client + s3 *s3.Client + bucket string + + scope coredata.Scoper + Frameworks *FrameworkService Mitigations *MitigationService Tasks *TaskService Evidences *EvidenceService - Peoples *PeopleService - Vendors *VendorService - Policies *PolicyService Organizations *OrganizationService + Vendors *VendorService + Peoples *PeopleService + Policies *PolicyService + Controls *ControlService } ) @@ -82,5 +85,7 @@ func (s *Service) WithTenant(tenantID gid.TenantID) *TenantService { tenantService.Vendors = &VendorService{svc: tenantService} tenantService.Policies = &PolicyService{svc: tenantService} tenantService.Organizations = &OrganizationService{svc: tenantService} + tenantService.Controls = &ControlService{svc: tenantService} + return tenantService } diff --git a/pkg/server/api/console/v1/schema.graphql b/pkg/server/api/console/v1/schema.graphql index f95d595c9..93b061334 100644 --- a/pkg/server/api/console/v1/schema.graphql +++ b/pkg/server/api/console/v1/schema.graphql @@ -155,6 +155,14 @@ type Organization implements Node { orderBy: PolicyOrder ): PolicyConnection! @goField(forceResolver: true) + mitigations( + first: Int + after: CursorKey + last: Int + before: CursorKey + orderBy: MitigationOrder + ): MitigationConnection! @goField(forceResolver: true) + createdAt: Datetime! updatedAt: Datetime! } @@ -186,7 +194,18 @@ enum FrameworkOrderField @goModel( model: "github.com/getprobo/probo/pkg/coredata.FrameworkOrderField" ) { - NAME + CREATED_AT + @goEnum( + value: "github.com/getprobo/probo/pkg/coredata.FrameworkOrderFieldCreatedAt" + ) +} + +enum ControlOrderField + @goModel(model: "github.com/getprobo/probo/pkg/coredata.ControlOrderField") { + CREATED_AT + @goEnum( + value: "github.com/getprobo/probo/pkg/coredata.ControlOrderFieldCreatedAt" + ) } enum MitigationOrderField @@ -235,6 +254,14 @@ input FrameworkOrder field: FrameworkOrderField! } +input ControlOrder + @goModel( + model: "github.com/getprobo/probo/pkg/server/api/console/v1/types.ControlOrderBy" + ) { + direction: OrderDirection! + field: ControlOrderField! +} + input MitigationOrder @goModel( model: "github.com/getprobo/probo/pkg/server/api/console/v1/types.MitigationOrderBy" @@ -285,7 +312,6 @@ type People implements Node { kind: PeopleKind! createdAt: Datetime! updatedAt: Datetime! - version: Int! } type VendorConnection { @@ -311,7 +337,6 @@ type Vendor implements Node { privacyPolicyUrl: String createdAt: Datetime! updatedAt: Datetime! - version: Int! } type FrameworkConnection { @@ -326,23 +351,40 @@ type FrameworkEdge { type Framework implements Node { id: ID! - version: Int! - name: String! description: String! - mitigations( + controls( first: Int after: CursorKey last: Int before: CursorKey - orderBy: MitigationOrder - ): MitigationConnection! @goField(forceResolver: true) + orderBy: ControlOrder + ): ControlConnection! @goField(forceResolver: true) createdAt: Datetime! updatedAt: Datetime! } +type ControlConnection { + edges: [ControlEdge!]! + pageInfo: PageInfo! +} + +type ControlEdge { + cursor: CursorKey! + node: Control! +} + +type Control implements Node { + id: ID! + referenceId: String! + name: String! + description: String! + createdAt: Datetime! + updatedAt: Datetime! +} + type MitigationConnection { edges: [MitigationEdge!]! pageInfo: PageInfo! @@ -355,7 +397,6 @@ type MitigationEdge { type Mitigation implements Node { id: ID! - version: Int! category: String! name: String! description: String! @@ -386,7 +427,6 @@ type TaskEdge { type Task implements Node { id: ID! - version: Int! name: String! description: String! state: TaskState! @@ -506,6 +546,7 @@ type Mutation { createFramework(input: CreateFrameworkInput!): CreateFrameworkPayload! updateFramework(input: UpdateFrameworkInput!): UpdateFrameworkPayload! importFramework(input: ImportFrameworkInput!): ImportFrameworkPayload! + deleteFramework(input: DeleteFrameworkInput!): DeleteFrameworkPayload! createMitigation(input: CreateMitigationInput!): CreateMitigationPayload! updateMitigation(input: UpdateMitigationInput!): UpdateMitigationPayload! @@ -553,7 +594,6 @@ input CreatePeopleInput { input UpdatePeopleInput { id: ID! - expectedVersion: Int! fullName: String primaryEmailAddress: String additionalEmailAddresses: [String!] @@ -588,7 +628,6 @@ enum RiskTier input UpdateVendorInput { id: ID! - expectedVersion: Int! name: String description: String serviceStartAt: Datetime @@ -662,6 +701,14 @@ type DeleteTaskPayload { deletedTaskId: ID! } +input DeleteFrameworkInput { + frameworkId: ID! +} + +type DeleteFrameworkPayload { + deletedFrameworkId: ID! +} + input CreateFrameworkInput { organizationId: ID! name: String! @@ -670,7 +717,6 @@ input CreateFrameworkInput { input UpdateFrameworkInput { id: ID! - expectedVersion: Int! name: String description: String } @@ -680,7 +726,7 @@ type CreateFrameworkPayload { } input CreateMitigationInput { - frameworkId: ID! + organizationId: ID! name: String! description: String! category: String! @@ -705,7 +751,6 @@ type UpdatePeoplePayload { input UpdateMitigationInput { id: ID! - expectedVersion: Int! name: String description: String category: String @@ -757,7 +802,6 @@ input CreatePolicyInput { input UpdatePolicyInput { id: ID! - expectedVersion: Int! name: String content: String status: PolicyStatus @@ -783,7 +827,6 @@ type DeletePolicyPayload { type Policy implements Node { id: ID! - version: Int! name: String! status: PolicyStatus! content: String! @@ -805,7 +848,6 @@ type PolicyEdge { input UpdateTaskInput { taskId: ID! - expectedVersion: Int! name: String description: String state: TaskState diff --git a/pkg/server/api/console/v1/schema/schema.go b/pkg/server/api/console/v1/schema/schema.go index 4c782c0a5..33285f349 100644 --- a/pkg/server/api/console/v1/schema/schema.go +++ b/pkg/server/api/console/v1/schema/schema.go @@ -65,6 +65,25 @@ type ComplexityRoot struct { Success func(childComplexity int) int } + Control struct { + CreatedAt func(childComplexity int) int + Description func(childComplexity int) int + ID func(childComplexity int) int + Name func(childComplexity int) int + ReferenceID func(childComplexity int) int + UpdatedAt func(childComplexity int) int + } + + ControlConnection struct { + Edges func(childComplexity int) int + PageInfo func(childComplexity int) int + } + + ControlEdge struct { + Cursor func(childComplexity int) int + Node func(childComplexity int) int + } + CreateFrameworkPayload struct { FrameworkEdge func(childComplexity int) int } @@ -97,6 +116,10 @@ type ComplexityRoot struct { DeletedEvidenceID func(childComplexity int) int } + DeleteFrameworkPayload struct { + DeletedFrameworkID func(childComplexity int) int + } + DeleteOrganizationPayload struct { DeletedOrganizationID func(childComplexity int) int } @@ -142,13 +165,12 @@ type ComplexityRoot struct { } Framework struct { + Controls func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) int CreatedAt func(childComplexity int) int Description func(childComplexity int) int ID func(childComplexity int) int - Mitigations func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MitigationOrderBy) int Name func(childComplexity int) int UpdatedAt func(childComplexity int) int - Version func(childComplexity int) int } FrameworkConnection struct { @@ -179,7 +201,6 @@ type ComplexityRoot struct { State func(childComplexity int) int Tasks func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.TaskOrderBy) int UpdatedAt func(childComplexity int) int - Version func(childComplexity int) int } MitigationConnection struct { @@ -203,6 +224,7 @@ type ComplexityRoot struct { CreateTask func(childComplexity int, input types.CreateTaskInput) int CreateVendor func(childComplexity int, input types.CreateVendorInput) int DeleteEvidence func(childComplexity int, input types.DeleteEvidenceInput) int + DeleteFramework func(childComplexity int, input types.DeleteFrameworkInput) int DeleteOrganization func(childComplexity int, input types.DeleteOrganizationInput) int DeletePeople func(childComplexity int, input types.DeletePeopleInput) int DeletePolicy func(childComplexity int, input types.DeletePolicyInput) int @@ -223,16 +245,17 @@ type ComplexityRoot struct { } Organization struct { - CreatedAt func(childComplexity int) int - Frameworks func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.FrameworkOrderBy) int - ID func(childComplexity int) int - LogoURL func(childComplexity int) int - Name func(childComplexity int) int - Peoples func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.PeopleOrderBy) int - Policies func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.PolicyOrderBy) int - UpdatedAt func(childComplexity int) int - Users func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.UserOrderBy) int - Vendors func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.VendorOrderBy) int + CreatedAt func(childComplexity int) int + Frameworks func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.FrameworkOrderBy) int + ID func(childComplexity int) int + LogoURL func(childComplexity int) int + Mitigations func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MitigationOrderBy) int + Name func(childComplexity int) int + Peoples func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.PeopleOrderBy) int + Policies func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.PolicyOrderBy) int + UpdatedAt func(childComplexity int) int + Users func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.UserOrderBy) int + Vendors func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.VendorOrderBy) int } OrganizationConnection struct { @@ -260,7 +283,6 @@ type ComplexityRoot struct { Kind func(childComplexity int) int PrimaryEmailAddress func(childComplexity int) int UpdatedAt func(childComplexity int) int - Version func(childComplexity int) int } PeopleConnection struct { @@ -282,7 +304,6 @@ type ComplexityRoot struct { ReviewDate func(childComplexity int) int Status func(childComplexity int) int UpdatedAt func(childComplexity int) int - Version func(childComplexity int) int } PolicyConnection struct { @@ -319,7 +340,6 @@ type ComplexityRoot struct { State func(childComplexity int) int TimeEstimate func(childComplexity int) int UpdatedAt func(childComplexity int) int - Version func(childComplexity int) int } TaskConnection struct { @@ -399,7 +419,6 @@ type ComplexityRoot struct { StatusPageURL func(childComplexity int) int TermsOfServiceURL func(childComplexity int) int UpdatedAt func(childComplexity int) int - Version func(childComplexity int) int } VendorConnection struct { @@ -423,7 +442,7 @@ type EvidenceResolver interface { FileURL(ctx context.Context, obj *types.Evidence) (*string, error) } type FrameworkResolver interface { - Mitigations(ctx context.Context, obj *types.Framework, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MitigationOrderBy) (*types.MitigationConnection, error) + Controls(ctx context.Context, obj *types.Framework, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error) } type MitigationResolver interface { Tasks(ctx context.Context, obj *types.Mitigation, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.TaskOrderBy) (*types.TaskConnection, error) @@ -446,6 +465,7 @@ type MutationResolver interface { CreateFramework(ctx context.Context, input types.CreateFrameworkInput) (*types.CreateFrameworkPayload, error) UpdateFramework(ctx context.Context, input types.UpdateFrameworkInput) (*types.UpdateFrameworkPayload, error) ImportFramework(ctx context.Context, input types.ImportFrameworkInput) (*types.ImportFrameworkPayload, error) + DeleteFramework(ctx context.Context, input types.DeleteFrameworkInput) (*types.DeleteFrameworkPayload, error) CreateMitigation(ctx context.Context, input types.CreateMitigationInput) (*types.CreateMitigationPayload, error) UpdateMitigation(ctx context.Context, input types.UpdateMitigationInput) (*types.UpdateMitigationPayload, error) UploadEvidence(ctx context.Context, input types.UploadEvidenceInput) (*types.UploadEvidencePayload, error) @@ -464,6 +484,7 @@ type OrganizationResolver interface { Vendors(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.VendorOrderBy) (*types.VendorConnection, error) Peoples(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.PeopleOrderBy) (*types.PeopleConnection, error) Policies(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.PolicyOrderBy) (*types.PolicyConnection, error) + Mitigations(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MitigationOrderBy) (*types.MitigationConnection, error) } type PolicyResolver interface { Owner(ctx context.Context, obj *types.Policy) (*types.People, error) @@ -513,6 +534,76 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.ConfirmEmailPayload.Success(childComplexity), true + case "Control.createdAt": + if e.complexity.Control.CreatedAt == nil { + break + } + + return e.complexity.Control.CreatedAt(childComplexity), true + + case "Control.description": + if e.complexity.Control.Description == nil { + break + } + + return e.complexity.Control.Description(childComplexity), true + + case "Control.id": + if e.complexity.Control.ID == nil { + break + } + + return e.complexity.Control.ID(childComplexity), true + + case "Control.name": + if e.complexity.Control.Name == nil { + break + } + + return e.complexity.Control.Name(childComplexity), true + + case "Control.referenceId": + if e.complexity.Control.ReferenceID == nil { + break + } + + return e.complexity.Control.ReferenceID(childComplexity), true + + case "Control.updatedAt": + if e.complexity.Control.UpdatedAt == nil { + break + } + + return e.complexity.Control.UpdatedAt(childComplexity), true + + case "ControlConnection.edges": + if e.complexity.ControlConnection.Edges == nil { + break + } + + return e.complexity.ControlConnection.Edges(childComplexity), true + + case "ControlConnection.pageInfo": + if e.complexity.ControlConnection.PageInfo == nil { + break + } + + return e.complexity.ControlConnection.PageInfo(childComplexity), true + + case "ControlEdge.cursor": + if e.complexity.ControlEdge.Cursor == nil { + break + } + + return e.complexity.ControlEdge.Cursor(childComplexity), true + + case "ControlEdge.node": + if e.complexity.ControlEdge.Node == nil { + break + } + + return e.complexity.ControlEdge.Node(childComplexity), true + case "CreateFrameworkPayload.frameworkEdge": if e.complexity.CreateFrameworkPayload.FrameworkEdge == nil { break @@ -569,6 +660,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.DeleteEvidencePayload.DeletedEvidenceID(childComplexity), true + case "DeleteFrameworkPayload.deletedFrameworkId": + if e.complexity.DeleteFrameworkPayload.DeletedFrameworkID == nil { + break + } + + return e.complexity.DeleteFrameworkPayload.DeletedFrameworkID(childComplexity), true + case "DeleteOrganizationPayload.deletedOrganizationId": if e.complexity.DeleteOrganizationPayload.DeletedOrganizationID == nil { break @@ -709,6 +807,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.EvidenceEdge.Node(childComplexity), true + case "Framework.controls": + if e.complexity.Framework.Controls == nil { + break + } + + args, err := ec.field_Framework_controls_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Framework.Controls(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey), args["orderBy"].(*types.ControlOrderBy)), true + case "Framework.createdAt": if e.complexity.Framework.CreatedAt == nil { break @@ -730,18 +840,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Framework.ID(childComplexity), true - case "Framework.mitigations": - if e.complexity.Framework.Mitigations == nil { - break - } - - args, err := ec.field_Framework_mitigations_args(context.TODO(), rawArgs) - if err != nil { - return 0, false - } - - return e.complexity.Framework.Mitigations(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey), args["orderBy"].(*types.MitigationOrderBy)), true - case "Framework.name": if e.complexity.Framework.Name == nil { break @@ -756,13 +854,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Framework.UpdatedAt(childComplexity), true - case "Framework.version": - if e.complexity.Framework.Version == nil { - break - } - - return e.complexity.Framework.Version(childComplexity), true - case "FrameworkConnection.edges": if e.complexity.FrameworkConnection.Edges == nil { break @@ -873,13 +964,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mitigation.UpdatedAt(childComplexity), true - case "Mitigation.version": - if e.complexity.Mitigation.Version == nil { - break - } - - return e.complexity.Mitigation.Version(childComplexity), true - case "MitigationConnection.edges": if e.complexity.MitigationConnection.Edges == nil { break @@ -1028,6 +1112,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.DeleteEvidence(childComplexity, args["input"].(types.DeleteEvidenceInput)), true + case "Mutation.deleteFramework": + if e.complexity.Mutation.DeleteFramework == nil { + break + } + + args, err := ec.field_Mutation_deleteFramework_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.DeleteFramework(childComplexity, args["input"].(types.DeleteFrameworkInput)), true + case "Mutation.deleteOrganization": if e.complexity.Mutation.DeleteOrganization == nil { break @@ -1265,6 +1361,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Organization.LogoURL(childComplexity), true + case "Organization.mitigations": + if e.complexity.Organization.Mitigations == nil { + break + } + + args, err := ec.field_Organization_mitigations_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Organization.Mitigations(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey), args["orderBy"].(*types.MitigationOrderBy)), true + case "Organization.name": if e.complexity.Organization.Name == nil { break @@ -1432,13 +1540,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.People.UpdatedAt(childComplexity), true - case "People.version": - if e.complexity.People.Version == nil { - break - } - - return e.complexity.People.Version(childComplexity), true - case "PeopleConnection.edges": if e.complexity.PeopleConnection.Edges == nil { break @@ -1523,13 +1624,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Policy.UpdatedAt(childComplexity), true - case "Policy.version": - if e.complexity.Policy.Version == nil { - break - } - - return e.complexity.Policy.Version(childComplexity), true - case "PolicyConnection.edges": if e.complexity.PolicyConnection.Edges == nil { break @@ -1666,13 +1760,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Task.UpdatedAt(childComplexity), true - case "Task.version": - if e.complexity.Task.Version == nil { - break - } - - return e.complexity.Task.Version(childComplexity), true - case "TaskConnection.edges": if e.complexity.TaskConnection.Edges == nil { break @@ -1911,13 +1998,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Vendor.UpdatedAt(childComplexity), true - case "Vendor.version": - if e.complexity.Vendor.Version == nil { - break - } - - return e.complexity.Vendor.Version(childComplexity), true - case "VendorConnection.edges": if e.complexity.VendorConnection.Edges == nil { break @@ -1982,6 +2062,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { inputUnmarshalMap := graphql.BuildUnmarshalerMap( ec.unmarshalInputAssignTaskInput, ec.unmarshalInputConfirmEmailInput, + ec.unmarshalInputControlOrder, ec.unmarshalInputCreateFrameworkInput, ec.unmarshalInputCreateMitigationInput, ec.unmarshalInputCreateOrganizationInput, @@ -1990,6 +2071,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec.unmarshalInputCreateTaskInput, ec.unmarshalInputCreateVendorInput, ec.unmarshalInputDeleteEvidenceInput, + ec.unmarshalInputDeleteFrameworkInput, ec.unmarshalInputDeleteOrganizationInput, ec.unmarshalInputDeletePeopleInput, ec.unmarshalInputDeletePolicyInput, @@ -2270,6 +2352,14 @@ type Organization implements Node { orderBy: PolicyOrder ): PolicyConnection! @goField(forceResolver: true) + mitigations( + first: Int + after: CursorKey + last: Int + before: CursorKey + orderBy: MitigationOrder + ): MitigationConnection! @goField(forceResolver: true) + createdAt: Datetime! updatedAt: Datetime! } @@ -2301,7 +2391,18 @@ enum FrameworkOrderField @goModel( model: "github.com/getprobo/probo/pkg/coredata.FrameworkOrderField" ) { - NAME + CREATED_AT + @goEnum( + value: "github.com/getprobo/probo/pkg/coredata.FrameworkOrderFieldCreatedAt" + ) +} + +enum ControlOrderField + @goModel(model: "github.com/getprobo/probo/pkg/coredata.ControlOrderField") { + CREATED_AT + @goEnum( + value: "github.com/getprobo/probo/pkg/coredata.ControlOrderFieldCreatedAt" + ) } enum MitigationOrderField @@ -2350,6 +2451,14 @@ input FrameworkOrder field: FrameworkOrderField! } +input ControlOrder + @goModel( + model: "github.com/getprobo/probo/pkg/server/api/console/v1/types.ControlOrderBy" + ) { + direction: OrderDirection! + field: ControlOrderField! +} + input MitigationOrder @goModel( model: "github.com/getprobo/probo/pkg/server/api/console/v1/types.MitigationOrderBy" @@ -2400,7 +2509,6 @@ type People implements Node { kind: PeopleKind! createdAt: Datetime! updatedAt: Datetime! - version: Int! } type VendorConnection { @@ -2426,7 +2534,6 @@ type Vendor implements Node { privacyPolicyUrl: String createdAt: Datetime! updatedAt: Datetime! - version: Int! } type FrameworkConnection { @@ -2441,23 +2548,40 @@ type FrameworkEdge { type Framework implements Node { id: ID! - version: Int! - name: String! description: String! - mitigations( + controls( first: Int after: CursorKey last: Int before: CursorKey - orderBy: MitigationOrder - ): MitigationConnection! @goField(forceResolver: true) + orderBy: ControlOrder + ): ControlConnection! @goField(forceResolver: true) createdAt: Datetime! updatedAt: Datetime! } +type ControlConnection { + edges: [ControlEdge!]! + pageInfo: PageInfo! +} + +type ControlEdge { + cursor: CursorKey! + node: Control! +} + +type Control implements Node { + id: ID! + referenceId: String! + name: String! + description: String! + createdAt: Datetime! + updatedAt: Datetime! +} + type MitigationConnection { edges: [MitigationEdge!]! pageInfo: PageInfo! @@ -2470,7 +2594,6 @@ type MitigationEdge { type Mitigation implements Node { id: ID! - version: Int! category: String! name: String! description: String! @@ -2501,7 +2624,6 @@ type TaskEdge { type Task implements Node { id: ID! - version: Int! name: String! description: String! state: TaskState! @@ -2621,6 +2743,7 @@ type Mutation { createFramework(input: CreateFrameworkInput!): CreateFrameworkPayload! updateFramework(input: UpdateFrameworkInput!): UpdateFrameworkPayload! importFramework(input: ImportFrameworkInput!): ImportFrameworkPayload! + deleteFramework(input: DeleteFrameworkInput!): DeleteFrameworkPayload! createMitigation(input: CreateMitigationInput!): CreateMitigationPayload! updateMitigation(input: UpdateMitigationInput!): UpdateMitigationPayload! @@ -2668,7 +2791,6 @@ input CreatePeopleInput { input UpdatePeopleInput { id: ID! - expectedVersion: Int! fullName: String primaryEmailAddress: String additionalEmailAddresses: [String!] @@ -2703,7 +2825,6 @@ enum RiskTier input UpdateVendorInput { id: ID! - expectedVersion: Int! name: String description: String serviceStartAt: Datetime @@ -2777,6 +2898,14 @@ type DeleteTaskPayload { deletedTaskId: ID! } +input DeleteFrameworkInput { + frameworkId: ID! +} + +type DeleteFrameworkPayload { + deletedFrameworkId: ID! +} + input CreateFrameworkInput { organizationId: ID! name: String! @@ -2785,7 +2914,6 @@ input CreateFrameworkInput { input UpdateFrameworkInput { id: ID! - expectedVersion: Int! name: String description: String } @@ -2795,7 +2923,7 @@ type CreateFrameworkPayload { } input CreateMitigationInput { - frameworkId: ID! + organizationId: ID! name: String! description: String! category: String! @@ -2820,7 +2948,6 @@ type UpdatePeoplePayload { input UpdateMitigationInput { id: ID! - expectedVersion: Int! name: String description: String category: String @@ -2872,7 +2999,6 @@ input CreatePolicyInput { input UpdatePolicyInput { id: ID! - expectedVersion: Int! name: String content: String status: PolicyStatus @@ -2898,7 +3024,6 @@ type DeletePolicyPayload { type Policy implements Node { id: ID! - version: Int! name: String! status: PolicyStatus! content: String! @@ -2920,7 +3045,6 @@ type PolicyEdge { input UpdateTaskInput { taskId: ID! - expectedVersion: Int! name: String description: String state: TaskState @@ -3018,37 +3142,37 @@ var parsedSchema = gqlparser.MustLoadSchema(sources...) // region ***************************** args.gotpl ***************************** -func (ec *executionContext) field_Framework_mitigations_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { +func (ec *executionContext) field_Framework_controls_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field_Framework_mitigations_argsFirst(ctx, rawArgs) + arg0, err := ec.field_Framework_controls_argsFirst(ctx, rawArgs) if err != nil { return nil, err } args["first"] = arg0 - arg1, err := ec.field_Framework_mitigations_argsAfter(ctx, rawArgs) + arg1, err := ec.field_Framework_controls_argsAfter(ctx, rawArgs) if err != nil { return nil, err } args["after"] = arg1 - arg2, err := ec.field_Framework_mitigations_argsLast(ctx, rawArgs) + arg2, err := ec.field_Framework_controls_argsLast(ctx, rawArgs) if err != nil { return nil, err } args["last"] = arg2 - arg3, err := ec.field_Framework_mitigations_argsBefore(ctx, rawArgs) + arg3, err := ec.field_Framework_controls_argsBefore(ctx, rawArgs) if err != nil { return nil, err } args["before"] = arg3 - arg4, err := ec.field_Framework_mitigations_argsOrderBy(ctx, rawArgs) + arg4, err := ec.field_Framework_controls_argsOrderBy(ctx, rawArgs) if err != nil { return nil, err } args["orderBy"] = arg4 return args, nil } -func (ec *executionContext) field_Framework_mitigations_argsFirst( +func (ec *executionContext) field_Framework_controls_argsFirst( ctx context.Context, rawArgs map[string]any, ) (*int, error) { @@ -3061,7 +3185,7 @@ func (ec *executionContext) field_Framework_mitigations_argsFirst( return zeroVal, nil } -func (ec *executionContext) field_Framework_mitigations_argsAfter( +func (ec *executionContext) field_Framework_controls_argsAfter( ctx context.Context, rawArgs map[string]any, ) (*page.CursorKey, error) { @@ -3074,7 +3198,7 @@ func (ec *executionContext) field_Framework_mitigations_argsAfter( return zeroVal, nil } -func (ec *executionContext) field_Framework_mitigations_argsLast( +func (ec *executionContext) field_Framework_controls_argsLast( ctx context.Context, rawArgs map[string]any, ) (*int, error) { @@ -3087,7 +3211,7 @@ func (ec *executionContext) field_Framework_mitigations_argsLast( return zeroVal, nil } -func (ec *executionContext) field_Framework_mitigations_argsBefore( +func (ec *executionContext) field_Framework_controls_argsBefore( ctx context.Context, rawArgs map[string]any, ) (*page.CursorKey, error) { @@ -3100,16 +3224,16 @@ func (ec *executionContext) field_Framework_mitigations_argsBefore( return zeroVal, nil } -func (ec *executionContext) field_Framework_mitigations_argsOrderBy( +func (ec *executionContext) field_Framework_controls_argsOrderBy( ctx context.Context, rawArgs map[string]any, -) (*types.MitigationOrderBy, error) { +) (*types.ControlOrderBy, error) { ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("orderBy")) if tmp, ok := rawArgs["orderBy"]; ok { - return ec.unmarshalOMitigationOrder2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐMitigationOrderBy(ctx, tmp) + return ec.unmarshalOControlOrder2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐControlOrderBy(ctx, tmp) } - var zeroVal *types.MitigationOrderBy + var zeroVal *types.ControlOrderBy return zeroVal, nil } @@ -3438,6 +3562,29 @@ func (ec *executionContext) field_Mutation_deleteEvidence_argsInput( return zeroVal, nil } +func (ec *executionContext) field_Mutation_deleteFramework_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := ec.field_Mutation_deleteFramework_argsInput(ctx, rawArgs) + if err != nil { + return nil, err + } + args["input"] = arg0 + return args, nil +} +func (ec *executionContext) field_Mutation_deleteFramework_argsInput( + ctx context.Context, + rawArgs map[string]any, +) (types.DeleteFrameworkInput, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("input")) + if tmp, ok := rawArgs["input"]; ok { + return ec.unmarshalNDeleteFrameworkInput2githubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐDeleteFrameworkInput(ctx, tmp) + } + + var zeroVal types.DeleteFrameworkInput + return zeroVal, nil +} + func (ec *executionContext) field_Mutation_deleteOrganization_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -3924,6 +4071,101 @@ func (ec *executionContext) field_Organization_frameworks_argsOrderBy( return zeroVal, nil } +func (ec *executionContext) field_Organization_mitigations_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := ec.field_Organization_mitigations_argsFirst(ctx, rawArgs) + if err != nil { + return nil, err + } + args["first"] = arg0 + arg1, err := ec.field_Organization_mitigations_argsAfter(ctx, rawArgs) + if err != nil { + return nil, err + } + args["after"] = arg1 + arg2, err := ec.field_Organization_mitigations_argsLast(ctx, rawArgs) + if err != nil { + return nil, err + } + args["last"] = arg2 + arg3, err := ec.field_Organization_mitigations_argsBefore(ctx, rawArgs) + if err != nil { + return nil, err + } + args["before"] = arg3 + arg4, err := ec.field_Organization_mitigations_argsOrderBy(ctx, rawArgs) + if err != nil { + return nil, err + } + args["orderBy"] = arg4 + return args, nil +} +func (ec *executionContext) field_Organization_mitigations_argsFirst( + ctx context.Context, + rawArgs map[string]any, +) (*int, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("first")) + if tmp, ok := rawArgs["first"]; ok { + return ec.unmarshalOInt2ᚖint(ctx, tmp) + } + + var zeroVal *int + return zeroVal, nil +} + +func (ec *executionContext) field_Organization_mitigations_argsAfter( + ctx context.Context, + rawArgs map[string]any, +) (*page.CursorKey, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("after")) + if tmp, ok := rawArgs["after"]; ok { + return ec.unmarshalOCursorKey2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋpageᚐCursorKey(ctx, tmp) + } + + var zeroVal *page.CursorKey + return zeroVal, nil +} + +func (ec *executionContext) field_Organization_mitigations_argsLast( + ctx context.Context, + rawArgs map[string]any, +) (*int, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("last")) + if tmp, ok := rawArgs["last"]; ok { + return ec.unmarshalOInt2ᚖint(ctx, tmp) + } + + var zeroVal *int + return zeroVal, nil +} + +func (ec *executionContext) field_Organization_mitigations_argsBefore( + ctx context.Context, + rawArgs map[string]any, +) (*page.CursorKey, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("before")) + if tmp, ok := rawArgs["before"]; ok { + return ec.unmarshalOCursorKey2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋpageᚐCursorKey(ctx, tmp) + } + + var zeroVal *page.CursorKey + return zeroVal, nil +} + +func (ec *executionContext) field_Organization_mitigations_argsOrderBy( + ctx context.Context, + rawArgs map[string]any, +) (*types.MitigationOrderBy, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("orderBy")) + if tmp, ok := rawArgs["orderBy"]; ok { + return ec.unmarshalOMitigationOrder2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐMitigationOrderBy(ctx, tmp) + } + + var zeroVal *types.MitigationOrderBy + return zeroVal, nil +} + func (ec *executionContext) field_Organization_peoples_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -4681,8 +4923,6 @@ func (ec *executionContext) fieldContext_AssignTaskPayload_task(_ context.Contex switch field.Name { case "id": return ec.fieldContext_Task_id(ctx, field) - case "version": - return ec.fieldContext_Task_version(ctx, field) case "name": return ec.fieldContext_Task_name(ctx, field) case "description": @@ -4750,6 +4990,476 @@ func (ec *executionContext) fieldContext_ConfirmEmailPayload_success(_ context.C return fc, nil } +func (ec *executionContext) _Control_id(ctx context.Context, field graphql.CollectedField, obj *types.Control) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Control_id(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.ID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(gid.GID) + fc.Result = res + return ec.marshalNID2githubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Control_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Control", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Control_referenceId(ctx context.Context, field graphql.CollectedField, obj *types.Control) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Control_referenceId(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.ReferenceID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Control_referenceId(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Control", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Control_name(ctx context.Context, field graphql.CollectedField, obj *types.Control) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Control_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Control_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Control", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Control_description(ctx context.Context, field graphql.CollectedField, obj *types.Control) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Control_description(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Control_description(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Control", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Control_createdAt(ctx context.Context, field graphql.CollectedField, obj *types.Control) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Control_createdAt(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.CreatedAt, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(time.Time) + fc.Result = res + return ec.marshalNDatetime2timeᚐTime(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Control_createdAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Control", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Datetime does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Control_updatedAt(ctx context.Context, field graphql.CollectedField, obj *types.Control) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Control_updatedAt(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.UpdatedAt, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(time.Time) + fc.Result = res + return ec.marshalNDatetime2timeᚐTime(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Control_updatedAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Control", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Datetime does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ControlConnection_edges(ctx context.Context, field graphql.CollectedField, obj *types.ControlConnection) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ControlConnection_edges(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Edges, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*types.ControlEdge) + fc.Result = res + return ec.marshalNControlEdge2ᚕᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐControlEdgeᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ControlConnection_edges(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ControlConnection", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "cursor": + return ec.fieldContext_ControlEdge_cursor(ctx, field) + case "node": + return ec.fieldContext_ControlEdge_node(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ControlEdge", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _ControlConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *types.ControlConnection) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ControlConnection_pageInfo(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.PageInfo, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*types.PageInfo) + fc.Result = res + return ec.marshalNPageInfo2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐPageInfo(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ControlConnection_pageInfo(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ControlConnection", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "hasNextPage": + return ec.fieldContext_PageInfo_hasNextPage(ctx, field) + case "hasPreviousPage": + return ec.fieldContext_PageInfo_hasPreviousPage(ctx, field) + case "startCursor": + return ec.fieldContext_PageInfo_startCursor(ctx, field) + case "endCursor": + return ec.fieldContext_PageInfo_endCursor(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type PageInfo", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _ControlEdge_cursor(ctx context.Context, field graphql.CollectedField, obj *types.ControlEdge) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ControlEdge_cursor(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Cursor, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(page.CursorKey) + fc.Result = res + return ec.marshalNCursorKey2githubᚗcomᚋgetproboᚋproboᚋpkgᚋpageᚐCursorKey(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ControlEdge_cursor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ControlEdge", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type CursorKey does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ControlEdge_node(ctx context.Context, field graphql.CollectedField, obj *types.ControlEdge) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ControlEdge_node(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Node, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*types.Control) + fc.Result = res + return ec.marshalNControl2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐControl(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ControlEdge_node(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ControlEdge", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Control_id(ctx, field) + case "referenceId": + return ec.fieldContext_Control_referenceId(ctx, field) + case "name": + return ec.fieldContext_Control_name(ctx, field) + case "description": + return ec.fieldContext_Control_description(ctx, field) + case "createdAt": + return ec.fieldContext_Control_createdAt(ctx, field) + case "updatedAt": + return ec.fieldContext_Control_updatedAt(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Control", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _CreateFrameworkPayload_frameworkEdge(ctx context.Context, field graphql.CollectedField, obj *types.CreateFrameworkPayload) (ret graphql.Marshaler) { fc, err := ec.fieldContext_CreateFrameworkPayload_frameworkEdge(ctx, field) if err != nil { @@ -5144,6 +5854,50 @@ func (ec *executionContext) fieldContext_DeleteEvidencePayload_deletedEvidenceId return fc, nil } +func (ec *executionContext) _DeleteFrameworkPayload_deletedFrameworkId(ctx context.Context, field graphql.CollectedField, obj *types.DeleteFrameworkPayload) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeleteFrameworkPayload_deletedFrameworkId(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.DeletedFrameworkID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(gid.GID) + fc.Result = res + return ec.marshalNID2githubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_DeleteFrameworkPayload_deletedFrameworkId(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "DeleteFrameworkPayload", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _DeleteOrganizationPayload_deletedOrganizationId(ctx context.Context, field graphql.CollectedField, obj *types.DeleteOrganizationPayload) (ret graphql.Marshaler) { fc, err := ec.fieldContext_DeleteOrganizationPayload_deletedOrganizationId(ctx, field) if err != nil { @@ -6102,50 +6856,6 @@ func (ec *executionContext) fieldContext_Framework_id(_ context.Context, field g return fc, nil } -func (ec *executionContext) _Framework_version(ctx context.Context, field graphql.CollectedField, obj *types.Framework) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Framework_version(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { - ctx = rctx // use context from middleware stack in children - return obj.Version, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(int) - fc.Result = res - return ec.marshalNInt2int(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_Framework_version(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Framework", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Int does not have child fields") - }, - } - return fc, nil -} - func (ec *executionContext) _Framework_name(ctx context.Context, field graphql.CollectedField, obj *types.Framework) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Framework_name(ctx, field) if err != nil { @@ -6234,8 +6944,8 @@ func (ec *executionContext) fieldContext_Framework_description(_ context.Context return fc, nil } -func (ec *executionContext) _Framework_mitigations(ctx context.Context, field graphql.CollectedField, obj *types.Framework) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Framework_mitigations(ctx, field) +func (ec *executionContext) _Framework_controls(ctx context.Context, field graphql.CollectedField, obj *types.Framework) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Framework_controls(ctx, field) if err != nil { return graphql.Null } @@ -6248,7 +6958,7 @@ func (ec *executionContext) _Framework_mitigations(ctx context.Context, field gr }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Framework().Mitigations(rctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey), fc.Args["orderBy"].(*types.MitigationOrderBy)) + return ec.resolvers.Framework().Controls(rctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey), fc.Args["orderBy"].(*types.ControlOrderBy)) }) if err != nil { ec.Error(ctx, err) @@ -6260,12 +6970,12 @@ func (ec *executionContext) _Framework_mitigations(ctx context.Context, field gr } return graphql.Null } - res := resTmp.(*types.MitigationConnection) + res := resTmp.(*types.ControlConnection) fc.Result = res - return ec.marshalNMitigationConnection2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐMitigationConnection(ctx, field.Selections, res) + return ec.marshalNControlConnection2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐControlConnection(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Framework_mitigations(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Framework_controls(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Framework", Field: field, @@ -6274,11 +6984,11 @@ func (ec *executionContext) fieldContext_Framework_mitigations(ctx context.Conte Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { case "edges": - return ec.fieldContext_MitigationConnection_edges(ctx, field) + return ec.fieldContext_ControlConnection_edges(ctx, field) case "pageInfo": - return ec.fieldContext_MitigationConnection_pageInfo(ctx, field) + return ec.fieldContext_ControlConnection_pageInfo(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type MitigationConnection", field.Name) + return nil, fmt.Errorf("no field named %q was found under type ControlConnection", field.Name) }, } defer func() { @@ -6288,7 +6998,7 @@ func (ec *executionContext) fieldContext_Framework_mitigations(ctx context.Conte } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Framework_mitigations_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Framework_controls_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } @@ -6572,14 +7282,12 @@ func (ec *executionContext) fieldContext_FrameworkEdge_node(_ context.Context, f switch field.Name { case "id": return ec.fieldContext_Framework_id(ctx, field) - case "version": - return ec.fieldContext_Framework_version(ctx, field) case "name": return ec.fieldContext_Framework_name(ctx, field) case "description": return ec.fieldContext_Framework_description(ctx, field) - case "mitigations": - return ec.fieldContext_Framework_mitigations(ctx, field) + case "controls": + return ec.fieldContext_Framework_controls(ctx, field) case "createdAt": return ec.fieldContext_Framework_createdAt(ctx, field) case "updatedAt": @@ -6729,50 +7437,6 @@ func (ec *executionContext) fieldContext_Mitigation_id(_ context.Context, field return fc, nil } -func (ec *executionContext) _Mitigation_version(ctx context.Context, field graphql.CollectedField, obj *types.Mitigation) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Mitigation_version(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { - ctx = rctx // use context from middleware stack in children - return obj.Version, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(int) - fc.Result = res - return ec.marshalNInt2int(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_Mitigation_version(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Mitigation", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Int does not have child fields") - }, - } - return fc, nil -} - func (ec *executionContext) _Mitigation_category(ctx context.Context, field graphql.CollectedField, obj *types.Mitigation) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Mitigation_category(ctx, field) if err != nil { @@ -7331,8 +7995,6 @@ func (ec *executionContext) fieldContext_MitigationEdge_node(_ context.Context, switch field.Name { case "id": return ec.fieldContext_Mitigation_id(ctx, field) - case "version": - return ec.fieldContext_Mitigation_version(ctx, field) case "category": return ec.fieldContext_Mitigation_category(ctx, field) case "name": @@ -8359,6 +9021,65 @@ func (ec *executionContext) fieldContext_Mutation_importFramework(ctx context.Co return fc, nil } +func (ec *executionContext) _Mutation_deleteFramework(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_deleteFramework(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().DeleteFramework(rctx, fc.Args["input"].(types.DeleteFrameworkInput)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*types.DeleteFrameworkPayload) + fc.Result = res + return ec.marshalNDeleteFrameworkPayload2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐDeleteFrameworkPayload(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Mutation_deleteFramework(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "deletedFrameworkId": + return ec.fieldContext_DeleteFrameworkPayload_deletedFrameworkId(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type DeleteFrameworkPayload", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_deleteFramework_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Mutation_createMitigation(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Mutation_createMitigation(ctx, field) if err != nil { @@ -9383,6 +10104,67 @@ func (ec *executionContext) fieldContext_Organization_policies(ctx context.Conte return fc, nil } +func (ec *executionContext) _Organization_mitigations(ctx context.Context, field graphql.CollectedField, obj *types.Organization) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Organization_mitigations(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Organization().Mitigations(rctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey), fc.Args["orderBy"].(*types.MitigationOrderBy)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*types.MitigationConnection) + fc.Result = res + return ec.marshalNMitigationConnection2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐMitigationConnection(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Organization_mitigations(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Organization", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "edges": + return ec.fieldContext_MitigationConnection_edges(ctx, field) + case "pageInfo": + return ec.fieldContext_MitigationConnection_pageInfo(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type MitigationConnection", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Organization_mitigations_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Organization_createdAt(ctx context.Context, field graphql.CollectedField, obj *types.Organization) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Organization_createdAt(ctx, field) if err != nil { @@ -9674,6 +10456,8 @@ func (ec *executionContext) fieldContext_OrganizationEdge_node(_ context.Context return ec.fieldContext_Organization_peoples(ctx, field) case "policies": return ec.fieldContext_Organization_policies(ctx, field) + case "mitigations": + return ec.fieldContext_Organization_mitigations(ctx, field) case "createdAt": return ec.fieldContext_Organization_createdAt(ctx, field) case "updatedAt": @@ -10163,50 +10947,6 @@ func (ec *executionContext) fieldContext_People_updatedAt(_ context.Context, fie return fc, nil } -func (ec *executionContext) _People_version(ctx context.Context, field graphql.CollectedField, obj *types.People) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_People_version(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { - ctx = rctx // use context from middleware stack in children - return obj.Version, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(int) - fc.Result = res - return ec.marshalNInt2int(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_People_version(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "People", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Int does not have child fields") - }, - } - return fc, nil -} - func (ec *executionContext) _PeopleConnection_edges(ctx context.Context, field graphql.CollectedField, obj *types.PeopleConnection) (ret graphql.Marshaler) { fc, err := ec.fieldContext_PeopleConnection_edges(ctx, field) if err != nil { @@ -10408,8 +11148,6 @@ func (ec *executionContext) fieldContext_PeopleEdge_node(_ context.Context, fiel return ec.fieldContext_People_createdAt(ctx, field) case "updatedAt": return ec.fieldContext_People_updatedAt(ctx, field) - case "version": - return ec.fieldContext_People_version(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type People", field.Name) }, @@ -10461,50 +11199,6 @@ func (ec *executionContext) fieldContext_Policy_id(_ context.Context, field grap return fc, nil } -func (ec *executionContext) _Policy_version(ctx context.Context, field graphql.CollectedField, obj *types.Policy) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Policy_version(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { - ctx = rctx // use context from middleware stack in children - return obj.Version, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(int) - fc.Result = res - return ec.marshalNInt2int(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_Policy_version(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Policy", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Int does not have child fields") - }, - } - return fc, nil -} - func (ec *executionContext) _Policy_name(ctx context.Context, field graphql.CollectedField, obj *types.Policy) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Policy_name(ctx, field) if err != nil { @@ -10731,8 +11425,6 @@ func (ec *executionContext) fieldContext_Policy_owner(_ context.Context, field g return ec.fieldContext_People_createdAt(ctx, field) case "updatedAt": return ec.fieldContext_People_updatedAt(ctx, field) - case "version": - return ec.fieldContext_People_version(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type People", field.Name) }, @@ -11017,8 +11709,6 @@ func (ec *executionContext) fieldContext_PolicyEdge_node(_ context.Context, fiel switch field.Name { case "id": return ec.fieldContext_Policy_id(ctx, field) - case "version": - return ec.fieldContext_Policy_version(ctx, field) case "name": return ec.fieldContext_Policy_name(ctx, field) case "status": @@ -11454,50 +12144,6 @@ func (ec *executionContext) fieldContext_Task_id(_ context.Context, field graphq return fc, nil } -func (ec *executionContext) _Task_version(ctx context.Context, field graphql.CollectedField, obj *types.Task) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Task_version(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { - ctx = rctx // use context from middleware stack in children - return obj.Version, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(int) - fc.Result = res - return ec.marshalNInt2int(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_Task_version(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Task", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Int does not have child fields") - }, - } - return fc, nil -} - func (ec *executionContext) _Task_name(ctx context.Context, field graphql.CollectedField, obj *types.Task) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Task_name(ctx, field) if err != nil { @@ -11721,8 +12367,6 @@ func (ec *executionContext) fieldContext_Task_assignedTo(_ context.Context, fiel return ec.fieldContext_People_createdAt(ctx, field) case "updatedAt": return ec.fieldContext_People_updatedAt(ctx, field) - case "version": - return ec.fieldContext_People_version(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type People", field.Name) }, @@ -12068,8 +12712,6 @@ func (ec *executionContext) fieldContext_TaskEdge_node(_ context.Context, field switch field.Name { case "id": return ec.fieldContext_Task_id(ctx, field) - case "version": - return ec.fieldContext_Task_version(ctx, field) case "name": return ec.fieldContext_Task_name(ctx, field) case "description": @@ -12134,8 +12776,6 @@ func (ec *executionContext) fieldContext_UnassignTaskPayload_task(_ context.Cont switch field.Name { case "id": return ec.fieldContext_Task_id(ctx, field) - case "version": - return ec.fieldContext_Task_version(ctx, field) case "name": return ec.fieldContext_Task_name(ctx, field) case "description": @@ -12200,14 +12840,12 @@ func (ec *executionContext) fieldContext_UpdateFrameworkPayload_framework(_ cont switch field.Name { case "id": return ec.fieldContext_Framework_id(ctx, field) - case "version": - return ec.fieldContext_Framework_version(ctx, field) case "name": return ec.fieldContext_Framework_name(ctx, field) case "description": return ec.fieldContext_Framework_description(ctx, field) - case "mitigations": - return ec.fieldContext_Framework_mitigations(ctx, field) + case "controls": + return ec.fieldContext_Framework_controls(ctx, field) case "createdAt": return ec.fieldContext_Framework_createdAt(ctx, field) case "updatedAt": @@ -12260,8 +12898,6 @@ func (ec *executionContext) fieldContext_UpdateMitigationPayload_mitigation(_ co switch field.Name { case "id": return ec.fieldContext_Mitigation_id(ctx, field) - case "version": - return ec.fieldContext_Mitigation_version(ctx, field) case "category": return ec.fieldContext_Mitigation_category(ctx, field) case "name": @@ -12340,6 +12976,8 @@ func (ec *executionContext) fieldContext_UpdateOrganizationPayload_organization( return ec.fieldContext_Organization_peoples(ctx, field) case "policies": return ec.fieldContext_Organization_policies(ctx, field) + case "mitigations": + return ec.fieldContext_Organization_mitigations(ctx, field) case "createdAt": return ec.fieldContext_Organization_createdAt(ctx, field) case "updatedAt": @@ -12404,8 +13042,6 @@ func (ec *executionContext) fieldContext_UpdatePeoplePayload_people(_ context.Co return ec.fieldContext_People_createdAt(ctx, field) case "updatedAt": return ec.fieldContext_People_updatedAt(ctx, field) - case "version": - return ec.fieldContext_People_version(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type People", field.Name) }, @@ -12454,8 +13090,6 @@ func (ec *executionContext) fieldContext_UpdatePolicyPayload_policy(_ context.Co switch field.Name { case "id": return ec.fieldContext_Policy_id(ctx, field) - case "version": - return ec.fieldContext_Policy_version(ctx, field) case "name": return ec.fieldContext_Policy_name(ctx, field) case "status": @@ -12518,8 +13152,6 @@ func (ec *executionContext) fieldContext_UpdateTaskPayload_task(_ context.Contex switch field.Name { case "id": return ec.fieldContext_Task_id(ctx, field) - case "version": - return ec.fieldContext_Task_version(ctx, field) case "name": return ec.fieldContext_Task_name(ctx, field) case "description": @@ -12606,8 +13238,6 @@ func (ec *executionContext) fieldContext_UpdateVendorPayload_vendor(_ context.Co return ec.fieldContext_Vendor_createdAt(ctx, field) case "updatedAt": return ec.fieldContext_Vendor_updatedAt(ctx, field) - case "version": - return ec.fieldContext_Vendor_version(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type Vendor", field.Name) }, @@ -13605,50 +14235,6 @@ func (ec *executionContext) fieldContext_Vendor_updatedAt(_ context.Context, fie return fc, nil } -func (ec *executionContext) _Vendor_version(ctx context.Context, field graphql.CollectedField, obj *types.Vendor) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Vendor_version(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { - ctx = rctx // use context from middleware stack in children - return obj.Version, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(int) - fc.Result = res - return ec.marshalNInt2int(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_Vendor_version(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Vendor", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Int does not have child fields") - }, - } - return fc, nil -} - func (ec *executionContext) _VendorConnection_edges(ctx context.Context, field graphql.CollectedField, obj *types.VendorConnection) (ret graphql.Marshaler) { fc, err := ec.fieldContext_VendorConnection_edges(ctx, field) if err != nil { @@ -13860,8 +14446,6 @@ func (ec *executionContext) fieldContext_VendorEdge_node(_ context.Context, fiel return ec.fieldContext_Vendor_createdAt(ctx, field) case "updatedAt": return ec.fieldContext_Vendor_updatedAt(ctx, field) - case "version": - return ec.fieldContext_Vendor_version(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type Vendor", field.Name) }, @@ -16042,6 +16626,40 @@ func (ec *executionContext) unmarshalInputConfirmEmailInput(ctx context.Context, return it, nil } +func (ec *executionContext) unmarshalInputControlOrder(ctx context.Context, obj any) (types.ControlOrderBy, error) { + var it types.ControlOrderBy + asMap := map[string]any{} + for k, v := range obj.(map[string]any) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"direction", "field"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "direction": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("direction")) + data, err := ec.unmarshalNOrderDirection2githubᚗcomᚋgetproboᚋproboᚋpkgᚋpageᚐOrderDirection(ctx, v) + if err != nil { + return it, err + } + it.Direction = data + case "field": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("field")) + data, err := ec.unmarshalNControlOrderField2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐControlOrderField(ctx, v) + if err != nil { + return it, err + } + it.Field = data + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputCreateFrameworkInput(ctx context.Context, obj any) (types.CreateFrameworkInput, error) { var it types.CreateFrameworkInput asMap := map[string]any{} @@ -16090,20 +16708,20 @@ func (ec *executionContext) unmarshalInputCreateMitigationInput(ctx context.Cont asMap[k] = v } - fieldsInOrder := [...]string{"frameworkId", "name", "description", "category", "importance"} + fieldsInOrder := [...]string{"organizationId", "name", "description", "category", "importance"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { continue } switch k { - case "frameworkId": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("frameworkId")) + case "organizationId": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("organizationId")) data, err := ec.unmarshalNID2githubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, v) if err != nil { return it, err } - it.FrameworkID = data + it.OrganizationID = data case "name": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) data, err := ec.unmarshalNString2string(ctx, v) @@ -16454,6 +17072,33 @@ func (ec *executionContext) unmarshalInputDeleteEvidenceInput(ctx context.Contex return it, nil } +func (ec *executionContext) unmarshalInputDeleteFrameworkInput(ctx context.Context, obj any) (types.DeleteFrameworkInput, error) { + var it types.DeleteFrameworkInput + asMap := map[string]any{} + for k, v := range obj.(map[string]any) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"frameworkId"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "frameworkId": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("frameworkId")) + data, err := ec.unmarshalNID2githubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, v) + if err != nil { + return it, err + } + it.FrameworkID = data + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputDeleteOrganizationInput(ctx context.Context, obj any) (types.DeleteOrganizationInput, error) { var it types.DeleteOrganizationInput asMap := map[string]any{} @@ -16970,7 +17615,7 @@ func (ec *executionContext) unmarshalInputUpdateFrameworkInput(ctx context.Conte asMap[k] = v } - fieldsInOrder := [...]string{"id", "expectedVersion", "name", "description"} + fieldsInOrder := [...]string{"id", "name", "description"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -16984,13 +17629,6 @@ func (ec *executionContext) unmarshalInputUpdateFrameworkInput(ctx context.Conte return it, err } it.ID = data - case "expectedVersion": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("expectedVersion")) - data, err := ec.unmarshalNInt2int(ctx, v) - if err != nil { - return it, err - } - it.ExpectedVersion = data case "name": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) data, err := ec.unmarshalOString2ᚖstring(ctx, v) @@ -17018,7 +17656,7 @@ func (ec *executionContext) unmarshalInputUpdateMitigationInput(ctx context.Cont asMap[k] = v } - fieldsInOrder := [...]string{"id", "expectedVersion", "name", "description", "category", "state", "importance"} + fieldsInOrder := [...]string{"id", "name", "description", "category", "state", "importance"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -17032,13 +17670,6 @@ func (ec *executionContext) unmarshalInputUpdateMitigationInput(ctx context.Cont return it, err } it.ID = data - case "expectedVersion": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("expectedVersion")) - data, err := ec.unmarshalNInt2int(ctx, v) - if err != nil { - return it, err - } - it.ExpectedVersion = data case "name": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) data, err := ec.unmarshalOString2ᚖstring(ctx, v) @@ -17128,7 +17759,7 @@ func (ec *executionContext) unmarshalInputUpdatePeopleInput(ctx context.Context, asMap[k] = v } - fieldsInOrder := [...]string{"id", "expectedVersion", "fullName", "primaryEmailAddress", "additionalEmailAddresses", "kind"} + fieldsInOrder := [...]string{"id", "fullName", "primaryEmailAddress", "additionalEmailAddresses", "kind"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -17142,13 +17773,6 @@ func (ec *executionContext) unmarshalInputUpdatePeopleInput(ctx context.Context, return it, err } it.ID = data - case "expectedVersion": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("expectedVersion")) - data, err := ec.unmarshalNInt2int(ctx, v) - if err != nil { - return it, err - } - it.ExpectedVersion = data case "fullName": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("fullName")) data, err := ec.unmarshalOString2ᚖstring(ctx, v) @@ -17190,7 +17814,7 @@ func (ec *executionContext) unmarshalInputUpdatePolicyInput(ctx context.Context, asMap[k] = v } - fieldsInOrder := [...]string{"id", "expectedVersion", "name", "content", "status", "reviewDate", "ownerId"} + fieldsInOrder := [...]string{"id", "name", "content", "status", "reviewDate", "ownerId"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -17204,13 +17828,6 @@ func (ec *executionContext) unmarshalInputUpdatePolicyInput(ctx context.Context, return it, err } it.ID = data - case "expectedVersion": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("expectedVersion")) - data, err := ec.unmarshalNInt2int(ctx, v) - if err != nil { - return it, err - } - it.ExpectedVersion = data case "name": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) data, err := ec.unmarshalOString2ᚖstring(ctx, v) @@ -17259,7 +17876,7 @@ func (ec *executionContext) unmarshalInputUpdateTaskInput(ctx context.Context, o asMap[k] = v } - fieldsInOrder := [...]string{"taskId", "expectedVersion", "name", "description", "state", "timeEstimate"} + fieldsInOrder := [...]string{"taskId", "name", "description", "state", "timeEstimate"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -17273,13 +17890,6 @@ func (ec *executionContext) unmarshalInputUpdateTaskInput(ctx context.Context, o return it, err } it.TaskID = data - case "expectedVersion": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("expectedVersion")) - data, err := ec.unmarshalNInt2int(ctx, v) - if err != nil { - return it, err - } - it.ExpectedVersion = data case "name": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) data, err := ec.unmarshalOString2ᚖstring(ctx, v) @@ -17321,7 +17931,7 @@ func (ec *executionContext) unmarshalInputUpdateVendorInput(ctx context.Context, asMap[k] = v } - fieldsInOrder := [...]string{"id", "expectedVersion", "name", "description", "serviceStartAt", "serviceTerminationAt", "serviceCriticality", "riskTier", "statusPageUrl", "termsOfServiceUrl", "privacyPolicyUrl"} + fieldsInOrder := [...]string{"id", "name", "description", "serviceStartAt", "serviceTerminationAt", "serviceCriticality", "riskTier", "statusPageUrl", "termsOfServiceUrl", "privacyPolicyUrl"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -17335,13 +17945,6 @@ func (ec *executionContext) unmarshalInputUpdateVendorInput(ctx context.Context, return it, err } it.ID = data - case "expectedVersion": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("expectedVersion")) - data, err := ec.unmarshalNInt2int(ctx, v) - if err != nil { - return it, err - } - it.ExpectedVersion = data case "name": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) data, err := ec.unmarshalOString2ᚖstring(ctx, v) @@ -17577,6 +18180,13 @@ func (ec *executionContext) _Node(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } return ec._Framework(ctx, sel, obj) + case types.Control: + return ec._Control(ctx, sel, &obj) + case *types.Control: + if obj == nil { + return graphql.Null + } + return ec._Control(ctx, sel, obj) case types.Mitigation: return ec._Mitigation(ctx, sel, &obj) case *types.Mitigation: @@ -17699,6 +18309,158 @@ func (ec *executionContext) _ConfirmEmailPayload(ctx context.Context, sel ast.Se return out } +var controlImplementors = []string{"Control", "Node"} + +func (ec *executionContext) _Control(ctx context.Context, sel ast.SelectionSet, obj *types.Control) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, controlImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Control") + case "id": + out.Values[i] = ec._Control_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "referenceId": + out.Values[i] = ec._Control_referenceId(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "name": + out.Values[i] = ec._Control_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "description": + out.Values[i] = ec._Control_description(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "createdAt": + out.Values[i] = ec._Control_createdAt(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "updatedAt": + out.Values[i] = ec._Control_updatedAt(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var controlConnectionImplementors = []string{"ControlConnection"} + +func (ec *executionContext) _ControlConnection(ctx context.Context, sel ast.SelectionSet, obj *types.ControlConnection) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, controlConnectionImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ControlConnection") + case "edges": + out.Values[i] = ec._ControlConnection_edges(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "pageInfo": + out.Values[i] = ec._ControlConnection_pageInfo(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var controlEdgeImplementors = []string{"ControlEdge"} + +func (ec *executionContext) _ControlEdge(ctx context.Context, sel ast.SelectionSet, obj *types.ControlEdge) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, controlEdgeImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ControlEdge") + case "cursor": + out.Values[i] = ec._ControlEdge_cursor(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "node": + out.Values[i] = ec._ControlEdge_node(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var createFrameworkPayloadImplementors = []string{"CreateFrameworkPayload"} func (ec *executionContext) _CreateFrameworkPayload(ctx context.Context, sel ast.SelectionSet, obj *types.CreateFrameworkPayload) graphql.Marshaler { @@ -18011,6 +18773,45 @@ func (ec *executionContext) _DeleteEvidencePayload(ctx context.Context, sel ast. return out } +var deleteFrameworkPayloadImplementors = []string{"DeleteFrameworkPayload"} + +func (ec *executionContext) _DeleteFrameworkPayload(ctx context.Context, sel ast.SelectionSet, obj *types.DeleteFrameworkPayload) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, deleteFrameworkPayloadImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("DeleteFrameworkPayload") + case "deletedFrameworkId": + out.Values[i] = ec._DeleteFrameworkPayload_deletedFrameworkId(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var deleteOrganizationPayloadImplementors = []string{"DeleteOrganizationPayload"} func (ec *executionContext) _DeleteOrganizationPayload(ctx context.Context, sel ast.SelectionSet, obj *types.DeleteOrganizationPayload) graphql.Marshaler { @@ -18424,11 +19225,6 @@ func (ec *executionContext) _Framework(ctx context.Context, sel ast.SelectionSet if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } - case "version": - out.Values[i] = ec._Framework_version(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) - } case "name": out.Values[i] = ec._Framework_name(ctx, field, obj) if out.Values[i] == graphql.Null { @@ -18439,7 +19235,7 @@ func (ec *executionContext) _Framework(ctx context.Context, sel ast.SelectionSet if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } - case "mitigations": + case "controls": field := field innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { @@ -18448,7 +19244,7 @@ func (ec *executionContext) _Framework(ctx context.Context, sel ast.SelectionSet ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Framework_mitigations(ctx, field, obj) + res = ec._Framework_controls(ctx, field, obj) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -18690,11 +19486,6 @@ func (ec *executionContext) _Mitigation(ctx context.Context, sel ast.SelectionSe if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } - case "version": - out.Values[i] = ec._Mitigation_version(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) - } case "category": out.Values[i] = ec._Mitigation_category(ctx, field, obj) if out.Values[i] == graphql.Null { @@ -19015,6 +19806,13 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { out.Invalids++ } + case "deleteFramework": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_deleteFramework(ctx, field) + }) + if out.Values[i] == graphql.Null { + out.Invalids++ + } case "createMitigation": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { return ec._Mutation_createMitigation(ctx, field) @@ -19341,6 +20139,42 @@ func (ec *executionContext) _Organization(ctx context.Context, sel ast.Selection continue } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "mitigations": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Organization_mitigations(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "createdAt": out.Values[i] = ec._Organization_createdAt(ctx, field, obj) @@ -19557,11 +20391,6 @@ func (ec *executionContext) _People(ctx context.Context, sel ast.SelectionSet, o if out.Values[i] == graphql.Null { out.Invalids++ } - case "version": - out.Values[i] = ec._People_version(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -19689,11 +20518,6 @@ func (ec *executionContext) _Policy(ctx context.Context, sel ast.SelectionSet, o if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } - case "version": - out.Values[i] = ec._Policy_version(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) - } case "name": out.Values[i] = ec._Policy_name(ctx, field, obj) if out.Values[i] == graphql.Null { @@ -20061,11 +20885,6 @@ func (ec *executionContext) _Task(ctx context.Context, sel ast.SelectionSet, obj if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } - case "version": - out.Values[i] = ec._Task_version(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) - } case "name": out.Values[i] = ec._Task_name(ctx, field, obj) if out.Values[i] == graphql.Null { @@ -20830,11 +21649,6 @@ func (ec *executionContext) _Vendor(ctx context.Context, sel ast.SelectionSet, o if out.Values[i] == graphql.Null { out.Invalids++ } - case "version": - out.Values[i] = ec._Vendor_version(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -21414,6 +22228,109 @@ func (ec *executionContext) marshalNConfirmEmailPayload2ᚖgithubᚗcomᚋgetpro return ec._ConfirmEmailPayload(ctx, sel, v) } +func (ec *executionContext) marshalNControl2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐControl(ctx context.Context, sel ast.SelectionSet, v *types.Control) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._Control(ctx, sel, v) +} + +func (ec *executionContext) marshalNControlConnection2githubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐControlConnection(ctx context.Context, sel ast.SelectionSet, v types.ControlConnection) graphql.Marshaler { + return ec._ControlConnection(ctx, sel, &v) +} + +func (ec *executionContext) marshalNControlConnection2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐControlConnection(ctx context.Context, sel ast.SelectionSet, v *types.ControlConnection) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._ControlConnection(ctx, sel, v) +} + +func (ec *executionContext) marshalNControlEdge2ᚕᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐControlEdgeᚄ(ctx context.Context, sel ast.SelectionSet, v []*types.ControlEdge) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNControlEdge2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐControlEdge(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNControlEdge2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐControlEdge(ctx context.Context, sel ast.SelectionSet, v *types.ControlEdge) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._ControlEdge(ctx, sel, v) +} + +func (ec *executionContext) unmarshalNControlOrderField2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐControlOrderField(ctx context.Context, v any) (coredata.ControlOrderField, error) { + tmp, err := graphql.UnmarshalString(v) + res := unmarshalNControlOrderField2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐControlOrderField[tmp] + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNControlOrderField2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐControlOrderField(ctx context.Context, sel ast.SelectionSet, v coredata.ControlOrderField) graphql.Marshaler { + res := graphql.MarshalString(marshalNControlOrderField2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐControlOrderField[v]) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + } + return res +} + +var ( + unmarshalNControlOrderField2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐControlOrderField = map[string]coredata.ControlOrderField{ + "CREATED_AT": coredata.ControlOrderFieldCreatedAt, + } + marshalNControlOrderField2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐControlOrderField = map[coredata.ControlOrderField]string{ + coredata.ControlOrderFieldCreatedAt: "CREATED_AT", + } +) + func (ec *executionContext) unmarshalNCreateFrameworkInput2githubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐCreateFrameworkInput(ctx context.Context, v any) (types.CreateFrameworkInput, error) { res, err := ec.unmarshalInputCreateFrameworkInput(ctx, v) return res, graphql.ErrorOnPath(ctx, err) @@ -21596,6 +22513,25 @@ func (ec *executionContext) marshalNDeleteEvidencePayload2ᚖgithubᚗcomᚋgetp return ec._DeleteEvidencePayload(ctx, sel, v) } +func (ec *executionContext) unmarshalNDeleteFrameworkInput2githubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐDeleteFrameworkInput(ctx context.Context, v any) (types.DeleteFrameworkInput, error) { + res, err := ec.unmarshalInputDeleteFrameworkInput(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNDeleteFrameworkPayload2githubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐDeleteFrameworkPayload(ctx context.Context, sel ast.SelectionSet, v types.DeleteFrameworkPayload) graphql.Marshaler { + return ec._DeleteFrameworkPayload(ctx, sel, &v) +} + +func (ec *executionContext) marshalNDeleteFrameworkPayload2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐDeleteFrameworkPayload(ctx context.Context, sel ast.SelectionSet, v *types.DeleteFrameworkPayload) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._DeleteFrameworkPayload(ctx, sel, v) +} + func (ec *executionContext) unmarshalNDeleteOrganizationInput2githubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐDeleteOrganizationInput(ctx context.Context, v any) (types.DeleteOrganizationInput, error) { res, err := ec.unmarshalInputDeleteOrganizationInput(ctx, v) return res, graphql.ErrorOnPath(ctx, err) @@ -21921,12 +22857,12 @@ func (ec *executionContext) marshalNFrameworkEdge2ᚖgithubᚗcomᚋgetproboᚋp func (ec *executionContext) unmarshalNFrameworkOrderField2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐFrameworkOrderField(ctx context.Context, v any) (coredata.FrameworkOrderField, error) { tmp, err := graphql.UnmarshalString(v) - res := coredata.FrameworkOrderField(tmp) + res := unmarshalNFrameworkOrderField2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐFrameworkOrderField[tmp] return res, graphql.ErrorOnPath(ctx, err) } func (ec *executionContext) marshalNFrameworkOrderField2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐFrameworkOrderField(ctx context.Context, sel ast.SelectionSet, v coredata.FrameworkOrderField) graphql.Marshaler { - res := graphql.MarshalString(string(v)) + res := graphql.MarshalString(marshalNFrameworkOrderField2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐFrameworkOrderField[v]) if res == graphql.Null { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { ec.Errorf(ctx, "the requested element is null which the schema does not allow") @@ -21935,6 +22871,15 @@ func (ec *executionContext) marshalNFrameworkOrderField2githubᚗcomᚋgetprobo return res } +var ( + unmarshalNFrameworkOrderField2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐFrameworkOrderField = map[string]coredata.FrameworkOrderField{ + "CREATED_AT": coredata.FrameworkOrderFieldCreatedAt, + } + marshalNFrameworkOrderField2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐFrameworkOrderField = map[coredata.FrameworkOrderField]string{ + coredata.FrameworkOrderFieldCreatedAt: "CREATED_AT", + } +) + func (ec *executionContext) unmarshalNID2githubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx context.Context, v any) (gid.GID, error) { res, err := types.UnmarshalGIDScalar(v) return res, graphql.ErrorOnPath(ctx, err) @@ -23472,6 +24417,14 @@ func (ec *executionContext) marshalOBoolean2ᚖbool(ctx context.Context, sel ast return res } +func (ec *executionContext) unmarshalOControlOrder2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐControlOrderBy(ctx context.Context, v any) (*types.ControlOrderBy, error) { + if v == nil { + return nil, nil + } + res, err := ec.unmarshalInputControlOrder(ctx, v) + return &res, graphql.ErrorOnPath(ctx, err) +} + func (ec *executionContext) unmarshalOCursorKey2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋpageᚐCursorKey(ctx context.Context, v any) (*page.CursorKey, error) { if v == nil { return nil, nil diff --git a/pkg/server/api/console/v1/types/control.go b/pkg/server/api/console/v1/types/control.go new file mode 100644 index 000000000..df7a849c4 --- /dev/null +++ b/pkg/server/api/console/v1/types/control.go @@ -0,0 +1,55 @@ +// Copyright (c) 2025 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package types + +import ( + "github.com/getprobo/probo/pkg/coredata" + "github.com/getprobo/probo/pkg/page" +) + +type ( + ControlOrderBy OrderBy[coredata.ControlOrderField] +) + +func NewControlConnection(p *page.Page[*coredata.Control, coredata.ControlOrderField]) *ControlConnection { + var edges = make([]*ControlEdge, len(p.Data)) + + for i := range edges { + edges[i] = NewControlEdge(p.Data[i], p.Cursor.OrderBy.Field) + } + + return &ControlConnection{ + Edges: edges, + PageInfo: NewPageInfo(p), + } +} + +func NewControlEdge(c *coredata.Control, orderBy coredata.ControlOrderField) *ControlEdge { + return &ControlEdge{ + Cursor: c.CursorKey(orderBy), + Node: NewControl(c), + } +} + +func NewControl(c *coredata.Control) *Control { + return &Control{ + ID: c.ID, + ReferenceID: c.ReferenceID, + Name: c.Name, + Description: c.Description, + CreatedAt: c.CreatedAt, + UpdatedAt: c.UpdatedAt, + } +} diff --git a/pkg/server/api/console/v1/types/framework.go b/pkg/server/api/console/v1/types/framework.go index 2167306d9..9c54eff9c 100644 --- a/pkg/server/api/console/v1/types/framework.go +++ b/pkg/server/api/console/v1/types/framework.go @@ -46,7 +46,6 @@ func NewFrameworkEdge(f *coredata.Framework, orderBy coredata.FrameworkOrderFiel func NewFramework(f *coredata.Framework) *Framework { return &Framework{ ID: f.ID, - Version: f.Version, Name: f.Name, Description: f.Description, CreatedAt: f.CreatedAt, diff --git a/pkg/server/api/console/v1/types/mitigation.go b/pkg/server/api/console/v1/types/mitigation.go index f57b3baa8..c71f3dff8 100644 --- a/pkg/server/api/console/v1/types/mitigation.go +++ b/pkg/server/api/console/v1/types/mitigation.go @@ -46,7 +46,6 @@ func NewMitigationEdge(c *coredata.Mitigation, orderBy coredata.MitigationOrderF func NewMitigation(c *coredata.Mitigation) *Mitigation { return &Mitigation{ ID: c.ID, - Version: c.Version, Category: c.Category, Name: c.Name, Description: c.Description, diff --git a/pkg/server/api/console/v1/types/people.go b/pkg/server/api/console/v1/types/people.go index 8f77adf91..c6afb07e9 100644 --- a/pkg/server/api/console/v1/types/people.go +++ b/pkg/server/api/console/v1/types/people.go @@ -52,6 +52,5 @@ func NewPeople(p *coredata.People) *People { Kind: p.Kind, CreatedAt: p.CreatedAt, UpdatedAt: p.UpdatedAt, - Version: p.Version, } } diff --git a/pkg/server/api/console/v1/types/policy.go b/pkg/server/api/console/v1/types/policy.go index 3a9bff228..8878f2b74 100644 --- a/pkg/server/api/console/v1/types/policy.go +++ b/pkg/server/api/console/v1/types/policy.go @@ -45,7 +45,6 @@ func NewPolicyEdge(policy *coredata.Policy, orderBy coredata.PolicyOrderField) * func NewPolicy(policy *coredata.Policy) *Policy { return &Policy{ ID: policy.ID, - Version: policy.Version, Name: policy.Name, Content: policy.Content, CreatedAt: policy.CreatedAt, diff --git a/pkg/server/api/console/v1/types/task.go b/pkg/server/api/console/v1/types/task.go index f33c59c5e..c9553cad7 100644 --- a/pkg/server/api/console/v1/types/task.go +++ b/pkg/server/api/console/v1/types/task.go @@ -52,6 +52,5 @@ func NewTask(t *coredata.Task) *Task { TimeEstimate: t.TimeEstimate, CreatedAt: t.CreatedAt, UpdatedAt: t.UpdatedAt, - Version: t.Version, } } diff --git a/pkg/server/api/console/v1/types/types.go b/pkg/server/api/console/v1/types/types.go index 4630348a8..052522ec5 100644 --- a/pkg/server/api/console/v1/types/types.go +++ b/pkg/server/api/console/v1/types/types.go @@ -36,6 +36,28 @@ type ConfirmEmailPayload struct { Success bool `json:"success"` } +type Control struct { + ID gid.GID `json:"id"` + ReferenceID string `json:"referenceId"` + Name string `json:"name"` + Description string `json:"description"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +func (Control) IsNode() {} +func (this Control) GetID() gid.GID { return this.ID } + +type ControlConnection struct { + Edges []*ControlEdge `json:"edges"` + PageInfo *PageInfo `json:"pageInfo"` +} + +type ControlEdge struct { + Cursor page.CursorKey `json:"cursor"` + Node *Control `json:"node"` +} + type CreateFrameworkInput struct { OrganizationID gid.GID `json:"organizationId"` Name string `json:"name"` @@ -47,11 +69,11 @@ type CreateFrameworkPayload struct { } type CreateMitigationInput struct { - FrameworkID gid.GID `json:"frameworkId"` - Name string `json:"name"` - Description string `json:"description"` - Category string `json:"category"` - Importance coredata.MitigationImportance `json:"importance"` + OrganizationID gid.GID `json:"organizationId"` + Name string `json:"name"` + Description string `json:"description"` + Category string `json:"category"` + Importance coredata.MitigationImportance `json:"importance"` } type CreateMitigationPayload struct { @@ -128,6 +150,14 @@ type DeleteEvidencePayload struct { DeletedEvidenceID gid.GID `json:"deletedEvidenceId"` } +type DeleteFrameworkInput struct { + FrameworkID gid.GID `json:"frameworkId"` +} + +type DeleteFrameworkPayload struct { + DeletedFrameworkID gid.GID `json:"deletedFrameworkId"` +} + type DeleteOrganizationInput struct { OrganizationID gid.GID `json:"organizationId"` } @@ -196,13 +226,12 @@ type EvidenceEdge struct { } type Framework struct { - ID gid.GID `json:"id"` - Version int `json:"version"` - Name string `json:"name"` - Description string `json:"description"` - Mitigations *MitigationConnection `json:"mitigations"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` + ID gid.GID `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Controls *ControlConnection `json:"controls"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` } func (Framework) IsNode() {} @@ -239,7 +268,6 @@ type InviteUserPayload struct { type Mitigation struct { ID gid.GID `json:"id"` - Version int `json:"version"` Category string `json:"category"` Name string `json:"name"` Description string `json:"description"` @@ -267,16 +295,17 @@ type Mutation struct { } type Organization struct { - ID gid.GID `json:"id"` - Name string `json:"name"` - LogoURL *string `json:"logoUrl,omitempty"` - Users *UserConnection `json:"users"` - Frameworks *FrameworkConnection `json:"frameworks"` - Vendors *VendorConnection `json:"vendors"` - Peoples *PeopleConnection `json:"peoples"` - Policies *PolicyConnection `json:"policies"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` + ID gid.GID `json:"id"` + Name string `json:"name"` + LogoURL *string `json:"logoUrl,omitempty"` + Users *UserConnection `json:"users"` + Frameworks *FrameworkConnection `json:"frameworks"` + Vendors *VendorConnection `json:"vendors"` + Peoples *PeopleConnection `json:"peoples"` + Policies *PolicyConnection `json:"policies"` + Mitigations *MitigationConnection `json:"mitigations"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` } func (Organization) IsNode() {} @@ -312,7 +341,6 @@ type People struct { Kind coredata.PeopleKind `json:"kind"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` - Version int `json:"version"` } func (People) IsNode() {} @@ -330,7 +358,6 @@ type PeopleEdge struct { type Policy struct { ID gid.GID `json:"id"` - Version int `json:"version"` Name string `json:"name"` Status coredata.PolicyStatus `json:"status"` Content string `json:"content"` @@ -372,7 +399,6 @@ type Session struct { type Task struct { ID gid.GID `json:"id"` - Version int `json:"version"` Name string `json:"name"` Description string `json:"description"` State coredata.TaskState `json:"state"` @@ -405,10 +431,9 @@ type UnassignTaskPayload struct { } type UpdateFrameworkInput struct { - ID gid.GID `json:"id"` - ExpectedVersion int `json:"expectedVersion"` - Name *string `json:"name,omitempty"` - Description *string `json:"description,omitempty"` + ID gid.GID `json:"id"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` } type UpdateFrameworkPayload struct { @@ -416,13 +441,12 @@ type UpdateFrameworkPayload struct { } type UpdateMitigationInput struct { - ID gid.GID `json:"id"` - ExpectedVersion int `json:"expectedVersion"` - Name *string `json:"name,omitempty"` - Description *string `json:"description,omitempty"` - Category *string `json:"category,omitempty"` - State *coredata.MitigationState `json:"state,omitempty"` - Importance *coredata.MitigationImportance `json:"importance,omitempty"` + ID gid.GID `json:"id"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + Category *string `json:"category,omitempty"` + State *coredata.MitigationState `json:"state,omitempty"` + Importance *coredata.MitigationImportance `json:"importance,omitempty"` } type UpdateMitigationPayload struct { @@ -441,7 +465,6 @@ type UpdateOrganizationPayload struct { type UpdatePeopleInput struct { ID gid.GID `json:"id"` - ExpectedVersion int `json:"expectedVersion"` FullName *string `json:"fullName,omitempty"` PrimaryEmailAddress *string `json:"primaryEmailAddress,omitempty"` AdditionalEmailAddresses []string `json:"additionalEmailAddresses,omitempty"` @@ -453,13 +476,12 @@ type UpdatePeoplePayload struct { } type UpdatePolicyInput struct { - ID gid.GID `json:"id"` - ExpectedVersion int `json:"expectedVersion"` - Name *string `json:"name,omitempty"` - Content *string `json:"content,omitempty"` - Status *coredata.PolicyStatus `json:"status,omitempty"` - ReviewDate *time.Time `json:"reviewDate,omitempty"` - OwnerID *gid.GID `json:"ownerId,omitempty"` + ID gid.GID `json:"id"` + Name *string `json:"name,omitempty"` + Content *string `json:"content,omitempty"` + Status *coredata.PolicyStatus `json:"status,omitempty"` + ReviewDate *time.Time `json:"reviewDate,omitempty"` + OwnerID *gid.GID `json:"ownerId,omitempty"` } type UpdatePolicyPayload struct { @@ -467,12 +489,11 @@ type UpdatePolicyPayload struct { } type UpdateTaskInput struct { - TaskID gid.GID `json:"taskId"` - ExpectedVersion int `json:"expectedVersion"` - Name *string `json:"name,omitempty"` - Description *string `json:"description,omitempty"` - State *coredata.TaskState `json:"state,omitempty"` - TimeEstimate *time.Duration `json:"timeEstimate,omitempty"` + TaskID gid.GID `json:"taskId"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + State *coredata.TaskState `json:"state,omitempty"` + TimeEstimate *time.Duration `json:"timeEstimate,omitempty"` } type UpdateTaskPayload struct { @@ -481,7 +502,6 @@ type UpdateTaskPayload struct { type UpdateVendorInput struct { ID gid.GID `json:"id"` - ExpectedVersion int `json:"expectedVersion"` Name *string `json:"name,omitempty"` Description *string `json:"description,omitempty"` ServiceStartAt *time.Time `json:"serviceStartAt,omitempty"` @@ -544,7 +564,6 @@ type Vendor struct { PrivacyPolicyURL *string `json:"privacyPolicyUrl,omitempty"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` - Version int `json:"version"` } func (Vendor) IsNode() {} diff --git a/pkg/server/api/console/v1/types/vendor.go b/pkg/server/api/console/v1/types/vendor.go index 05b594d16..ffa7bfcfe 100644 --- a/pkg/server/api/console/v1/types/vendor.go +++ b/pkg/server/api/console/v1/types/vendor.go @@ -57,6 +57,5 @@ func NewVendor(v *coredata.Vendor) *Vendor { StatusPageURL: v.StatusPageURL, TermsOfServiceURL: v.TermsOfServiceURL, PrivacyPolicyURL: v.PrivacyPolicyURL, - Version: v.Version, } } diff --git a/pkg/server/api/console/v1/v1_resolver.go b/pkg/server/api/console/v1/v1_resolver.go index 619d2a44d..6902f09a8 100644 --- a/pkg/server/api/console/v1/v1_resolver.go +++ b/pkg/server/api/console/v1/v1_resolver.go @@ -36,28 +36,29 @@ func (r *evidenceResolver) FileURL(ctx context.Context, obj *types.Evidence) (*s return &result, nil } -// Mitigations is the resolver for the mitigations field. -func (r *frameworkResolver) Mitigations(ctx context.Context, obj *types.Framework, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MitigationOrderBy) (*types.MitigationConnection, error) { +// Controls is the resolver for the controls field. +func (r *frameworkResolver) Controls(ctx context.Context, obj *types.Framework, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error) { svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) - pageOrderBy := page.OrderBy[coredata.MitigationOrderField]{ - Field: coredata.MitigationOrderFieldCreatedAt, + pageOrderBy := page.OrderBy[coredata.ControlOrderField]{ + Field: coredata.ControlOrderFieldCreatedAt, Direction: page.OrderDirectionDesc, } if orderBy != nil { - pageOrderBy = page.OrderBy[coredata.MitigationOrderField]{ + pageOrderBy = page.OrderBy[coredata.ControlOrderField]{ Field: orderBy.Field, Direction: orderBy.Direction, } } cursor := types.NewCursor(first, after, last, before, pageOrderBy) - page, err := svc.Mitigations.ListForFrameworkID(ctx, obj.ID, cursor) + + page, err := svc.Controls.ListForFrameworkID(ctx, obj.ID, cursor) if err != nil { - return nil, fmt.Errorf("cannot list framework mitigations: %w", err) + return nil, fmt.Errorf("cannot list controls: %w", err) } - return types.NewMitigationConnection(page), nil + return types.NewControlConnection(page), nil } // Tasks is the resolver for the tasks field. @@ -115,7 +116,6 @@ func (r *mutationResolver) UpdateVendor(ctx context.Context, input types.UpdateV vendor, err := svc.Vendors.Update(ctx, probo.UpdateVendorRequest{ ID: input.ID, - ExpectedVersion: input.ExpectedVersion, Name: input.Name, Description: input.Description, ServiceStartAt: input.ServiceStartAt, @@ -176,7 +176,6 @@ func (r *mutationResolver) UpdatePeople(ctx context.Context, input types.UpdateP people, err := svc.Peoples.Update(ctx, probo.UpdatePeopleRequest{ ID: input.ID, - ExpectedVersion: input.ExpectedVersion, FullName: input.FullName, PrimaryEmailAddress: input.PrimaryEmailAddress, AdditionalEmailAddresses: &input.AdditionalEmailAddresses, @@ -281,12 +280,11 @@ func (r *mutationResolver) UpdateTask(ctx context.Context, input types.UpdateTas svc := r.GetTenantServiceIfAuthorized(ctx, input.TaskID.TenantID()) task, err := svc.Tasks.Update(ctx, probo.UpdateTaskRequest{ - TaskID: input.TaskID, - ExpectedVersion: input.ExpectedVersion, - Name: input.Name, - Description: input.Description, - State: input.State, - TimeEstimate: input.TimeEstimate, + TaskID: input.TaskID, + Name: input.Name, + Description: input.Description, + State: input.State, + TimeEstimate: input.TimeEstimate, }) if err != nil { return nil, fmt.Errorf("cannot update task: %w", err) @@ -346,7 +344,6 @@ func (r *mutationResolver) CreateFramework(ctx context.Context, input types.Crea framework, err := svc.Frameworks.Create(ctx, probo.CreateFrameworkRequest{ OrganizationID: input.OrganizationID, Name: input.Name, - Description: input.Description, }) if err != nil { return nil, fmt.Errorf("cannot create framework: %w", err) @@ -362,10 +359,9 @@ func (r *mutationResolver) UpdateFramework(ctx context.Context, input types.Upda svc := r.GetTenantServiceIfAuthorized(ctx, input.ID.TenantID()) framework, err := svc.Frameworks.Update(ctx, probo.UpdateFrameworkRequest{ - ID: input.ID, - ExpectedVersion: input.ExpectedVersion, - Name: input.Name, - Description: input.Description, + ID: input.ID, + Name: input.Name, + Description: input.Description, }) if err != nil { return nil, fmt.Errorf("cannot update framework: %w", err) @@ -376,12 +372,26 @@ func (r *mutationResolver) UpdateFramework(ctx context.Context, input types.Upda }, nil } +// DeleteFramework is the resolver for the deleteFramework field. +func (r *mutationResolver) DeleteFramework(ctx context.Context, input types.DeleteFrameworkInput) (*types.DeleteFrameworkPayload, error) { + svc := r.GetTenantServiceIfAuthorized(ctx, input.FrameworkID.TenantID()) + + err := svc.Frameworks.Delete(ctx, input.FrameworkID) + if err != nil { + return nil, fmt.Errorf("cannot delete framework: %w", err) + } + + return &types.DeleteFrameworkPayload{ + DeletedFrameworkID: input.FrameworkID, + }, nil +} + // ImportFramework is the resolver for the importFramework field. func (r *mutationResolver) ImportFramework(ctx context.Context, input types.ImportFrameworkInput) (*types.ImportFrameworkPayload, error) { svc := r.GetTenantServiceIfAuthorized(ctx, input.OrganizationID.TenantID()) req := probo.ImportFrameworkRequest{} - if err := json.NewDecoder(input.File.File).Decode(&req.Data); err != nil { + if err := json.NewDecoder(input.File.File).Decode(&req.Framework); err != nil { return nil, fmt.Errorf("cannot decode framework: %w", err) } @@ -395,16 +405,16 @@ func (r *mutationResolver) ImportFramework(ctx context.Context, input types.Impo }, nil } -// CreateMitigation is the resolver for the createMitigation field. +// // CreateMitigation is the resolver for the createMitigation field. func (r *mutationResolver) CreateMitigation(ctx context.Context, input types.CreateMitigationInput) (*types.CreateMitigationPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.FrameworkID.TenantID()) + svc := r.GetTenantServiceIfAuthorized(ctx, input.OrganizationID.TenantID()) mitigation, err := svc.Mitigations.Create(ctx, probo.CreateMitigationRequest{ - FrameworkID: input.FrameworkID, - Name: input.Name, - Description: input.Description, - Category: input.Category, - Importance: input.Importance, + OrganizationID: input.OrganizationID, + Name: input.Name, + Description: input.Description, + Category: input.Category, + Importance: input.Importance, }) if err != nil { panic(fmt.Errorf("cannot create mitigation: %w", err)) @@ -420,13 +430,12 @@ func (r *mutationResolver) UpdateMitigation(ctx context.Context, input types.Upd svc := r.GetTenantServiceIfAuthorized(ctx, input.ID.TenantID()) mitigation, err := svc.Mitigations.Update(ctx, probo.UpdateMitigationRequest{ - ID: input.ID, - ExpectedVersion: input.ExpectedVersion, - Name: input.Name, - Description: input.Description, - Category: input.Category, - Importance: input.Importance, - State: input.State, + ID: input.ID, + Name: input.Name, + Description: input.Description, + Category: input.Category, + Importance: input.Importance, + State: input.State, }) if err != nil { panic(fmt.Errorf("cannot update mitigation: %w", err)) @@ -515,13 +524,12 @@ func (r *mutationResolver) UpdatePolicy(ctx context.Context, input types.UpdateP svc := r.GetTenantServiceIfAuthorized(ctx, input.ID.TenantID()) policy, err := svc.Policies.Update(ctx, probo.UpdatePolicyRequest{ - ID: input.ID, - ExpectedVersion: input.ExpectedVersion, - Name: input.Name, - Content: input.Content, - Status: input.Status, - ReviewDate: input.ReviewDate, - OwnerID: input.OwnerID, + ID: input.ID, + Name: input.Name, + Content: input.Content, + Status: input.Status, + ReviewDate: input.ReviewDate, + OwnerID: input.OwnerID, }) if err != nil { return nil, fmt.Errorf("cannot update policy: %w", err) @@ -733,6 +741,31 @@ func (r *organizationResolver) Policies(ctx context.Context, obj *types.Organiza return types.NewPolicyConnection(page), nil } +// Mitigations is the resolver for the mitigations field. +func (r *organizationResolver) Mitigations(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MitigationOrderBy) (*types.MitigationConnection, error) { + svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + + pageOrderBy := page.OrderBy[coredata.MitigationOrderField]{ + Field: coredata.MitigationOrderFieldCreatedAt, + Direction: page.OrderDirectionDesc, + } + if orderBy != nil { + pageOrderBy = page.OrderBy[coredata.MitigationOrderField]{ + Field: orderBy.Field, + Direction: orderBy.Direction, + } + } + + cursor := types.NewCursor(first, after, last, before, pageOrderBy) + + page, err := svc.Mitigations.ListForOrganizationID(ctx, obj.ID, cursor) + if err != nil { + return nil, fmt.Errorf("cannot list organization mitigations: %w", err) + } + + return types.NewMitigationConnection(page), nil +} + // Owner is the resolver for the owner field. func (r *policyResolver) Owner(ctx context.Context, obj *types.Policy) (*types.People, error) { svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID())