Skip to content

Improvements, new tables#43

Merged
Marfuen merged 6 commits into
mainfrom
mariano/employees_table
Feb 11, 2025
Merged

Improvements, new tables#43
Marfuen merged 6 commits into
mainfrom
mariano/employees_table

Conversation

@Marfuen
Copy link
Copy Markdown
Contributor

@Marfuen Marfuen commented Feb 11, 2025

Summary by CodeRabbit

  • New Features

    • Enhanced dashboard with a new frameworks overview showing compliance progress through interactive, data-driven cards.
    • Introduced an intuitive framework selection grid that lets users easily choose, clear, and confirm their selections.
    • Rolled out an updated policies interface featuring simplified views with clear loading states and improved data presentation.
    • Expanded localization support for framework management, including error handling and loading states.
    • Added new components for displaying framework progress and policies overview, enhancing user engagement and navigation.
  • Bug Fixes

    • Resolved issues related to data fetching and error handling in the frameworks and policies sections.
  • Chores

    • Updated package dependencies and configurations for improved performance and maintainability.

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Feb 11, 2025

CLA assistant check
All committers have signed the CLA.

@vercel
Copy link
Copy Markdown

vercel Bot commented Feb 11, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
app ✅ Ready (Inspect) Visit Preview 💬 Add feedback Feb 11, 2025 9:41pm
web ✅ Ready (Inspect) Visit Preview 💬 Add feedback Feb 11, 2025 9:41pm

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 11, 2025

Walkthrough

This update introduces new actions and React components to handle frameworks and policies, along with custom hooks for data fetching using SWR. The changes include modifications to authentication and error handling in the framework and policy actions, updates to database seeding scripts and SQL migrations, and expanded localization support. Several schema and JSON files have been added or altered to support SOC 2, ISO 27001, and GDPR frameworks while streamlining the policy management and compliance data flow.

Changes

File(s) Change Summary
apps/app/src/actions/framework/get-frameworks-action.ts
apps/app/src/actions/framework/select-frameworks-action.ts
Added new framework actions for retrieving and selecting frameworks using authentication, Zod validation, and parallel DB queries.
apps/app/src/actions/organization/create-organization-action.ts
apps/app/src/actions/soc2-seed.ts
Removed SOC2 seeding functionality and commented-out default policy creation, reflecting a shift in compliance process.
apps/app/src/actions/types.ts Introduced new type ActionData<T> for structured action responses.
apps/app/src/app/[locale]/**/Components/FrameworkProgress.tsx
apps/app/src/app/[locale]/**/Components/FrameworksGrid.tsx
apps/app/src/app/[locale]/**/Components/FrameworksOverview.tsx
Added React components for displaying framework progress, grid selection for frameworks, and an overall overview with internationalization support.
apps/app/src/app/[locale]/**/hooks/useFrameworks.ts Implemented a custom hook to fetch and manage framework data through new actions.
apps/app/src/app/[locale]/**/Components/PoliciesOverview.tsx
apps/app/src/app/[locale]/**/loading.tsx
apps/app/src/app/[locale]/**/page.tsx
apps/app/src/app/[locale]/**/Actions/get-policies.ts
apps/app/src/app/[locale]/**/Components/PoliciesTable.tsx
Added and refactored policies components and actions to streamline policy data fetching and presentation using SWR.
apps/app/src/app/[locale]/**/hooks/usePolicies.ts (both policies overview and policies all versions) Added custom hooks for fetching and filtering policy data with pagination, searching, and sorting.
apps/app/src/layout.tsx Updated the layout styling to include horizontal centering and vertical padding.
apps/app/src/auth/config.ts
apps/app/src/auth/org.ts
Removed imports of soc2Seed to reflect changes in compliance seeding routines.
apps/app/src/locales/{en,es,fr,no,pt}.ts Expanded localization entries under the frameworks key for overview, progress, and grid sections.
packages/data/{categories,controls,frameworks,policies}/**.json Added new JSON files for SOC2 categories, controls, frameworks (SOC 2, ISO 27001, GDPR), and password policy definitions.
packages/db/prisma/migrations/**
packages/db/prisma/schema.prisma
Introduced several SQL migration scripts and updated the Prisma schema with new enums, tables, and columns related to policies, controls, and organization policy management.
packages/db/prisma/seed.{js,ts}
packages/db/prisma/seedTypes.{js,ts}
Added new database seeding scripts and TypeScript interfaces for frameworks, controls, and policy-related data.

Sequence Diagram(s)

sequenceDiagram
    participant U as User
    participant DP as DashboardPage
    participant FO as FrameworksOverview
    participant UH as useFrameworks Hook
    participant GFA as getFrameworksAction
    participant DB as Database
    U->>DP: Navigate to Dashboard
    DP->>FO: Render FrameworksOverview
    FO->>UH: Request frameworks data
    UH->>GFA: Invoke getFrameworksAction
    GFA->>DB: Execute parallel queries for org-specific & available frameworks
    DB-->>GFA: Return frameworks data
    GFA-->>UH: Provide frameworks data
    UH-->>FO: Supply data to component
    FO-->>DP: Display updated framework view
Loading
sequenceDiagram
    participant U as User
    participant POP as PoliciesOverviewPage
    participant PO as PoliciesOverview
    participant PH as usePolicies Hook
    participant GP as getPolicies Function
    participant DB as Database
    U->>POP: Navigate to Policies Page
    POP->>PO: Render PoliciesOverview
    PO->>PH: Request policies data
    PH->>GP: Invoke getPolicies
    GP->>DB: Query organization policies
    DB-->>GP: Return policies data
    GP-->>PH: Provide policies data
    PH-->>PO: Supply data for rendering
    PO-->>POP: Display policies overview
Loading

Poem

I'm a bunny tapping keys with glee,
Hopping through code as fast as can be.
Frameworks and policies dance in the light,
DB seeds and migrations working just right.
From schema to component, every line sings,
A code-filled garden where happiness springs!
(_/)(•ㅅ•)♡

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

🔭 Outside diff range comments (4)
packages/db/prisma/migrations/20250210222417_change_used_by_datatype/migration.sql (1)

1-10: ⚠️ Potential issue

Add data preservation strategy for usedBy column.

This migration will fail if there is existing data in the usedBy column. Consider adding steps to:

  1. Backup existing data
  2. Transform data to new JSONB format
  3. Restore transformed data after column change

Add these steps before the ALTER TABLE:

-- Backup existing data
CREATE TABLE IF NOT EXISTS policy_used_by_backup AS 
SELECT id, usedBy FROM "Policy";

-- After column recreation, restore transformed data
-- Add appropriate JSON transformation logic here

Run this script to check for existing data that needs preservation:

❌ Verification inconclusive

🏁 Script executed:

#!/bin/bash
# Check if Policy table has any rows with non-null usedBy values
echo "SELECT COUNT(*) FROM \"Policy\" WHERE usedBy IS NOT NULL;" | psql $DATABASE_URL

Length of output: 166


Manually verify data preservation for the Policy.usedBy column

The executed script did not yield any useful output because the psql command wasn’t available. Please re-run the verification in an environment with a working SQL client to confirm whether any rows currently have non-null usedBy values. If data exists, a backup and transformation strategy must be added before altering the column.

packages/db/prisma/migrations/20250207011314_add_policy_id/migration.sql (1)

1-11: ⚠️ Potential issue

Migration name doesn't match changes and data loss risk.

Two issues with this migration:

  1. The migration name suggests adding policy_id but the changes only drop columns

  2. Data in fileType and url columns will be permanently lost

  3. Rename migration to reflect actual changes:

-- Rename to: 20250207011314_remove_file_type_and_url.sql
  1. Add data preservation:
-- Backup data before dropping
CREATE TABLE control_requirement_backup AS 
SELECT id, fileType, url FROM "ControlRequirement";

Check for existing data that would be lost:

❌ Verification inconclusive

🏁 Script executed:

#!/bin/bash
echo "SELECT COUNT(*) FROM \"ControlRequirement\" WHERE fileType IS NOT NULL OR url IS NOT NULL;" | psql $DATABASE_URL

Length of output: 199


Migration file naming and data backup verification

The migration continues to drop the fileType and url columns without a corresponding backup step, and the filename does not reflect the changes. The verification command did not run due to the missing psql tool. Please manually verify if there’s any existing data in these columns using the appropriate database connection, and consider renaming the migration file and adding a backup step.

packages/db/prisma/migrations/20250206231115_simplify_controls_table/migration.sql (1)

1-17: 💡 Verification agent

🧩 Analysis chain

Verify data migration and backup procedures before executing this destructive migration.

This migration will result in data loss as it drops two columns from the Control table. Please ensure that:

  1. Any required data from these columns has been migrated to new tables/columns.
  2. A database backup is taken before executing this migration.
  3. The application code has been updated to handle these schema changes.

Run the following script to check for existing data in these columns:


🏁 Script executed:

#!/bin/bash
# Description: Check for existing data in columns that will be dropped

# Test: Search for any references to the columns in the codebase
rg -A 5 'categoryId|requiredArtifactTypes'

# Test: Search for any migration files that might contain the data migration logic
fd -e sql -e ts -e js | rg -l 'categoryId|requiredArtifactTypes'

Length of output: 31658


Destructive Migration Risks – Confirm Data Backup, Migration, and Code Updates

The migration drops the “categoryId” and “requiredArtifactTypes” columns from the “Control” table, but our search shows that these columns are still referenced in several parts of the codebase. Please ensure you:

  • Migrate any critical data from these columns to new tables/columns if needed.
  • Take a full database backup before executing the migration.
  • Update all application code still referencing these columns (e.g., in:
    • apps/app/src/actions/framework/get-framework-categories.ts
    • apps/app/src/app/[locale]/(app)/(dashboard)/frameworks/[frameworkId]/page.tsx
    • apps/app/src/components/tables/frameworks/columns.tsx
    • apps/app/src/lib/soc2-controls.ts
    • packages/data/controls/soc2.json) so that the app no longer depends on them.
packages/db/prisma/schema.prisma (1)

113-148: 🛠️ Refactor suggestion

Add validation for the subdomain field.

The subdomain field is marked as unique but lacks validation to ensure it contains only valid characters and follows subdomain naming conventions.

Consider implementing validation at the application level. Example using Zod:

import { z } from 'zod';

export const SubdomainSchema = z
  .string()
  .min(1)
  .max(63)
  .regex(/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/, {
    message: 'Subdomain must contain only lowercase letters, numbers, and hyphens, and cannot start or end with a hyphen'
  });

export type Subdomain = z.infer<typeof SubdomainSchema>;
🧹 Nitpick comments (28)
apps/app/src/actions/soc2-seed.ts (1)

12-12: Confirm the removal of SOC 2 seed logic.

This function is now empty, effectively removing all SOC 2 seeding behavior. If this is intentional, ensure that nothing in the system depends on SOC 2 data being seeded. Otherwise, consider reintroducing or migrating this logic elsewhere.

apps/app/src/actions/organization/create-organization-action.ts (1)

98-105: Revisit the removal of default policies creation.

These lines are commented out, which disables the automatic creation of default policies when an organization is created. If you rely on default policies for onboarding or compliance, reintroduce or replace this logic to avoid leaving new organizations without necessary policies.

packages/db/prisma/seed.js (1)

1-1: Consider removing redundant "use strict" directive.

Modern Node.js or TypeScript modules typically run in strict mode by default. If this file is truly a commonJS module that needs strict mode enforced manually, you may keep it. Otherwise, it's unnecessary, and you can safely remove it.

🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)


[error] 1-4: Use let or const instead of var.

A variable declared with var is accessible in the whole module. Thus, the variable can be accessed before its initialization and outside the block where it is declared.
See MDN web docs for more details.
Unsafe fix: Use 'const' instead.

(lint/style/noVar)

apps/app/src/app/[locale]/(app)/(dashboard)/policies/(overview)/loading.tsx (1)

1-15: Consider using dynamic heights for skeletons.

The implementation looks good and follows React best practices. However, consider making the skeleton heights dynamic to better match the actual content dimensions.

-        <Skeleton className="h-[200px] w-full" />
-        <Skeleton className="h-[200px] w-full" />
+        <Skeleton className="h-[var(--content-height)] w-full" />
+        <Skeleton className="h-[var(--content-height)] w-full" />
apps/app/src/app/[locale]/(app)/(dashboard)/policies/(overview)/hooks/usePolicies.ts (2)

7-8: Consider moving the key constant to a dedicated constants file.

For better maintainability and reusability, consider moving POLICIES_OVERVIEW_KEY to a dedicated constants file.


9-17: Enhance error handling with specific error types.

Consider creating specific error types for different failure scenarios to improve error handling and debugging.

+type PolicyError = {
+  type: 'FETCH_ERROR' | 'VALIDATION_ERROR';
+  message: string;
+};

 async function fetchPolicies(): Promise<OrganizationPolicy[]> {
   const response = await getPolicies({});

   if (!response?.data?.success || !response.data.data) {
-    throw new Error(response?.data?.error || "Failed to fetch policy data");
+    throw {
+      type: response?.data ? 'VALIDATION_ERROR' : 'FETCH_ERROR',
+      message: response?.data?.error || "Failed to fetch policy data"
+    } as PolicyError;
   }

   return response.data.data;
 }
apps/app/src/app/[locale]/(app)/(dashboard)/policies/(overview)/Actions/get-policies.ts (1)

48-53: Improve error message with error details.

Consider including more specific error details in the error message to aid debugging.

     } catch (error) {
       return {
         success: false,
-        error: "Failed to fetch policy statistics",
+        error: `Failed to fetch policy statistics: ${error instanceof Error ? error.message : 'Unknown error'}`,
       };
     }
apps/app/src/app/[locale]/(app)/(dashboard)/policies/(overview)/Components/PoliciesOverview.tsx (1)

13-15: Enhance error message for better user experience.

The current error message directly exposes the error object to users. Consider providing a more user-friendly message.

-      <div className="p-4 text-red-500">
-        Failed to load policy data: {error.message}
-      </div>
+      <div className="p-4 text-red-500 rounded border border-red-200 bg-red-50">
+        <p>Unable to load policy data. Please try again later or contact support if the issue persists.</p>
+        {process.env.NODE_ENV === 'development' && <p className="mt-2 text-sm">{error.message}</p>}
+      </div>
apps/app/src/app/[locale]/(app)/(dashboard)/policies/all/Actions/get-policies.ts (1)

58-62: Enhance error handling with detailed logging.

The catch block uses a generic error message. Consider logging more details for debugging while keeping the user-facing message generic.

     } catch (error) {
+      console.error("[getPolicies] Failed to fetch policies:", {
+        userId: user.id,
+        organizationId: user.organizationId,
+        error,
+      });
       return {
         success: false,
         error: "Failed to fetch policies",
       };
     }
apps/app/src/actions/framework/get-frameworks-action.ts (2)

41-51: Optimize database queries with caching.

The frameworks query could benefit from caching as framework data doesn't change frequently.

Consider implementing Redis caching for the availableFrameworks query to improve performance:

// Add to imports
import { redis } from '@bubba/redis';

// Inside try block
const [frameworks, availableFrameworks] = await Promise.all([
  db.organizationFramework.findMany({
    where: { organizationId: user.organizationId },
    include: {
      organizationControl: true,
      framework: true,
    },
  }),
  redis.get('available_frameworks').then(async (cached) => {
    if (cached) return JSON.parse(cached);
    const frameworks = await db.framework.findMany();
    await redis.setex('available_frameworks', 3600, JSON.stringify(frameworks));
    return frameworks;
  }),
]);

57-61: Enhance error logging with context.

The error logging could be more detailed to help with debugging.

     } catch (error) {
-      console.error("Error fetching frameworks:", error);
+      console.error("[getFrameworksAction] Failed to fetch frameworks:", {
+        userId: user.id,
+        organizationId: user.organizationId,
+        error,
+      });
       return {
         error: "Failed to fetch frameworks",
       };
     }
apps/app/src/app/[locale]/(app)/(dashboard)/policies/all/Components/PoliciesTable.tsx (2)

33-39: Enhance error message presentation.

The error message could be more user-friendly and accessible.

-      <div className="p-4 text-red-500">
-        Failed to load policies: {error.message}
-      </div>
+      <div 
+        role="alert"
+        aria-live="polite"
+        className="p-4 text-red-500 rounded border border-red-200 bg-red-50"
+      >
+        <p>Unable to load policies. Please try again later or contact support if the issue persists.</p>
+        {process.env.NODE_ENV === 'development' && <p className="mt-2 text-sm">{error.message}</p>}
+      </div>

52-63: Optimize component performance with memoization.

The rendered components could benefit from memoization to prevent unnecessary re-renders.

+  const memoizedFilterToolbar = useMemo(
+    () => <FilterToolbar users={users} />,
+    [users]
+  );
+
+  const memoizedDataTable = useMemo(
+    () => (
+      <DataTable
+        data={data.items}
+        columnHeaders={columnHeaders}
+        pageCount={pageCount}
+        currentPage={Number(page)}
+      />
+    ),
+    [data.items, columnHeaders, pageCount, page]
+  );
+
   return (
     <div className="space-y-4">
-      <FilterToolbar users={users} />
-
-      <DataTable
-        data={data.items}
-        columnHeaders={columnHeaders}
-        pageCount={pageCount}
-        currentPage={Number(page)}
-      />
+      {memoizedFilterToolbar}
+      {memoizedDataTable}
     </div>
   );
apps/app/src/app/[locale]/(app)/(dashboard)/(home)/hooks/useFrameworks.ts (2)

9-22: Enhance type safety and error handling.

Consider these improvements to make the code more robust:

  1. Replace type casting with type guards
  2. Add more specific error messages
 async function fetchFrameworks(): Promise<FrameworksResponse> {
   const result = await getFrameworksAction();
 
   if (!result) {
-    throw new Error("Failed to fetch frameworks");
+    throw new Error("Framework action returned no response");
   }
 
-  const data = result.data?.data as FrameworksResponse | undefined;
-  if (!data) {
-    throw new Error("Invalid response from server");
+  const responseData = result.data?.data;
+  if (!responseData || !isFrameworksResponse(responseData)) {
+    throw new Error("Server response missing required framework data");
   }
 
-  return data;
+  return responseData;
 }
+
+function isFrameworksResponse(data: unknown): data is FrameworksResponse {
+  return (
+    typeof data === "object" &&
+    data !== null &&
+    "frameworks" in data &&
+    "availableFrameworks" in data
+  );
+}

24-67: Consider adding retry options and error typing.

The hook implementation is solid, but could be enhanced with:

  1. SWR retry options for failed requests
  2. Typed error handling
+interface FrameworkError extends Error {
+  code?: string;
+}
+
 export function useFrameworks() {
   const {
     data,
     error,
     isLoading,
     mutate: revalidateFrameworks,
   } = useSWR<FrameworksResponse>("frameworks", () => fetchFrameworks(), {
     revalidateOnFocus: false,
     revalidateOnReconnect: false,
+    retryCount: 3,
+    retryDelay: (retryCount) => Math.min(1000 * 2 ** retryCount, 30000),
   });
 
   const [isMutating, setIsMutating] = useState(false);
 
   const selectFrameworks = useCallback(
     async (frameworkIds: string[]) => {
       setIsMutating(true);
       try {
         const result = await selectFrameworksAction({ frameworkIds });
 
         if (!result?.data) {
           throw new Error("Failed to select frameworks");
         }
 
         await revalidateFrameworks();
-      } catch (err) {
+      } catch (err: unknown) {
+        const error = err as FrameworkError;
         console.error("selectFrameworksAction failed:", err);
-        throw err;
+        throw error;
       } finally {
         setIsMutating(false);
       }
     },
     [revalidateFrameworks]
   );
apps/app/src/app/[locale]/(app)/(dashboard)/(home)/Components/FrameworksOverview.tsx (1)

10-68: Enhance accessibility and testability.

Consider adding ARIA labels and test IDs to improve accessibility and testability.

 export const FrameworksOverview = () => {
   // ... existing code ...
   if (error) {
     return (
-      <div className="flex items-center justify-center min-h-[50vh]">
+      <div 
+        className="flex items-center justify-center min-h-[50vh]"
+        role="alert"
+        aria-label={t("frameworks.overview.error")}
+        data-testid="frameworks-error"
+      >
         <p className="text-destructive">{t("frameworks.overview.error")}</p>
       </div>
     );
   }
 
   if (isLoading) {
     return (
-      <div className="space-y-12">
+      <div 
+        className="space-y-12"
+        role="status"
+        aria-label={t("frameworks.overview.loading")}
+        data-testid="frameworks-loading"
+      >
         <div className="grid gap-4 md:grid-cols-2">
           <Skeleton className="h-[400px] w-full" />
           <Skeleton className="h-[400px] w-full" />
         </div>
       </div>
     );
   }
apps/app/src/app/[locale]/(app)/(dashboard)/policies/all/hooks/usePolicies.ts (1)

35-77: Enhance type safety and performance of filtering and sorting.

Consider these improvements:

  1. Add type safety for sort parameters
  2. Memoize the filter function to prevent unnecessary recalculations
+type SortField = "name" | "lastUpdated";
+type SortDirection = "asc" | "desc";
+
+interface SortOptions {
+  field: SortField;
+  direction: SortDirection;
+}
+
+function parseSortString(sort: string): SortOptions | null {
+  const [field, direction] = sort.split(":");
+  if (
+    !field ||
+    !direction ||
+    !["name", "lastUpdated"].includes(field) ||
+    !["asc", "desc"].includes(direction)
+  ) {
+    return null;
+  }
+  return {
+    field: field as SortField,
+    direction: direction as SortDirection,
+  };
+}
+
 function filterAndSortPolicies(
   data: PoliciesResponse | undefined,
   { search, sort, status }: Partial<UsePoliciesOptions>
 ): PoliciesResponse | undefined {
   if (!data) return undefined;
 
   let filteredItems = [...data.items];
 
   // Apply search filter
   if (search) {
     const searchLower = search.toLowerCase();
     filteredItems = filteredItems.filter((item) =>
       item.policy.name.toLowerCase().includes(searchLower)
     );
   }
 
   // Apply status filter
   if (status && status !== "all") {
     filteredItems = filteredItems.filter((item) => item.status === status);
   }
 
   // Apply sorting
   if (sort) {
-    const [field, direction] = sort.split(":");
-    const multiplier = direction === "desc" ? -1 : 1;
+    const sortOptions = parseSortString(sort);
+    if (sortOptions) {
+      const { field, direction } = sortOptions;
+      const multiplier = direction === "desc" ? -1 : 1;
 
-    filteredItems.sort((a, b) => {
-      switch (field) {
-        case "name":
-          return multiplier * a.policy.name.localeCompare(b.policy.name);
-        case "lastUpdated":
-          return multiplier * (a.updatedAt.getTime() - b.updatedAt.getTime());
-        default:
-          return 0;
-      }
-    });
+      filteredItems.sort((a, b) => {
+        switch (field) {
+          case "name":
+            return multiplier * a.policy.name.localeCompare(b.policy.name);
+          case "lastUpdated":
+            return multiplier * (a.updatedAt.getTime() - b.updatedAt.getTime());
+        }
+      });
+    }
   }
apps/app/src/app/[locale]/(app)/(dashboard)/(home)/Components/FrameworkProgress.tsx (2)

31-45: Consider adding loading state handling.

The empty state UI is well-implemented, but there's no handling for loading states which could lead to a jarring user experience during data fetching.

Consider adding a loading skeleton:

 {frameworks.length === 0 ? (
+  isLoading ? (
+    <div className="animate-pulse space-y-4">
+      <div className="h-12 w-12 bg-muted rounded-full mx-auto" />
+      <div className="h-4 w-48 bg-muted rounded mx-auto" />
+      <div className="h-4 w-64 bg-muted rounded mx-auto" />
+    </div>
+  ) : (
   <div className="flex flex-col items-center justify-center py-8 text-center">

56-70: Consider accessibility improvements.

While the component is functional, it could benefit from improved accessibility.

Add ARIA attributes for better screen reader support:

 <Link
   key={framework.id}
   href={`/frameworks/${framework.framework.id}`}
+  aria-label={`${framework.framework.name} - ${Math.round(progress)}% complete`}
   className="block space-y-3 rounded-lg p-4 hover:bg-muted transition-colors duration-200"
 >
apps/app/src/app/[locale]/(app)/(dashboard)/(home)/Components/FrameworksGrid.tsx (1)

76-82: Consider separating click handler for better maintainability.

The inline click handler could be moved to a separate function for better readability and maintainability.

Extract the click handler:

+const toggleFramework = (frameworkId: string) => {
+  setSelectedFrameworks((prev) =>
+    prev.includes(frameworkId)
+      ? prev.filter((id) => id !== frameworkId)
+      : [...prev, frameworkId]
+  );
+};

-onClick={() => {
-  setSelectedFrameworks((prev) =>
-    prev.includes(framework.id)
-      ? prev.filter((id) => id !== framework.id)
-      : [...prev, framework.id]
-  );
-}}
+onClick={() => toggleFramework(framework.id)}
packages/db/prisma/seed.ts (3)

9-32: Consider wrapping the seeding operations in a transaction.

While the seeding sequence is well-structured, wrapping the operations in a transaction would ensure data consistency in case of failures during the seeding process.

 async function main() {
+  await prisma.$transaction(async (tx) => {
     console.log("\n🗑️  Cleaning up existing data...");
     // For testing, we will delete all existing data, except for the users.
-    await prisma.framework.deleteMany();
-    await prisma.frameworkCategory.deleteMany();
-    await prisma.policy.deleteMany();
-    await prisma.control.deleteMany();
-    await prisma.controlRequirement.deleteMany();
+    await tx.framework.deleteMany();
+    await tx.frameworkCategory.deleteMany();
+    await tx.policy.deleteMany();
+    await tx.control.deleteMany();
+    await tx.controlRequirement.deleteMany();
     console.log("✅ Database cleaned");

     console.log("\n📋 Seeding policies...");
-    await seedPolicies();
+    await seedPolicies(tx);
     console.log("✅ Policies seeded");

     console.log("\n🏗️  Seeding frameworks...");
-    await seedFrameworks();
+    await seedFrameworks(tx);
     console.log("✅ Frameworks seeded");

     console.log("\n🔗 Seeding policy frameworks...");
-    await seedPolicyFramework();
+    await seedPolicyFramework(tx);
     console.log("✅ Policy frameworks seeded");

     console.log("\n🎉 All data seeded successfully!");
+  });
 }

112-161: Add data validation for framework categories.

While the function handles missing files well, it should also validate category data before upserting.

 async function seedFrameworkCategories(frameworkId: string) {
   let categories: string;

   try {
     categories = fs.readFileSync(
       join(__dirname, `../../data/categories/${frameworkId}.json`),
       "utf8"
     );
   } catch (error) {
     console.log(
       `  ⚠️  No categories found for framework ${frameworkId}, skipping`
     );
     return;
   }

+  let categoriesData: Record<string, FrameworkCategory>;
   try {
-    const categoriesData = JSON.parse(categories) as Record<
-      string,
-      FrameworkCategory
-    >;
+    categoriesData = JSON.parse(categories) as Record<string, FrameworkCategory>;
+
+    // Validate category data structure
+    for (const [code, data] of Object.entries(categoriesData)) {
+      if (!data.name || !data.code) {
+        throw new Error(`Invalid category data for code ${code}`);
+      }
+    }
+  } catch (error) {
+    console.error(`  ❌ Error parsing categories for framework ${frameworkId}:`, error);
+    return;
+  }

   console.log(
     `    📑 Found ${Object.keys(categoriesData).length} categories for ${frameworkId}`
   );

   // Upsert the framework categories for the given framework.
   for (const [categoryCode, categoryData] of Object.entries(categoriesData)) {
     console.log(`      ⏳ Processing category: ${categoryData.name}...`);
     try {
       // First, upsert the framework category itself for the given framework.
       await prisma.frameworkCategory.upsert({
         where: { id: categoryCode },
         update: {
           name: categoryData.name,
           code: categoryData.code,
           description: categoryData.description,
           frameworkId: frameworkId,
         },
         create: {
           id: categoryCode,
           name: categoryData.name,
           description: categoryData.description,
           code: categoryData.code,
           frameworkId: frameworkId,
         },
       });

       // Then, upsert the controls for the given framework category.
       await seedFrameworkCategoryControls(frameworkId, categoryCode);
       console.log(`      ✅ Category ${categoryData.name} processed`);
+    } catch (error) {
+      console.error(`      ❌ Error processing category ${categoryData.name}:`, error);
+    }
   }
 }

34-41: Enhance error handling with detailed error information.

Improve error reporting to help diagnose seeding failures.

 main()
   .catch((e) => {
-    console.error("\n❌ Error during seeding:", e);
+    console.error("\n❌ Error during seeding:");
+    if (e instanceof Error) {
+      console.error("Message:", e.message);
+      console.error("Stack:", e.stack);
+    } else {
+      console.error("Unknown error:", e);
+    }
     process.exit(1);
   })
   .finally(async () => {
     await prisma.$disconnect();
   });
packages/db/prisma/migrations/20250210212236_connect_category_to_controls/migration.sql (1)

1-9: Consider data backfill strategy for existing controls.

The migration adds a relationship between controls and categories but doesn't handle existing control records. Consider adding a data backfill strategy if controls should be associated with categories.

Add backfill logic after the foreign key:

-- Example backfill based on control identifiers
UPDATE "Control" 
SET "frameworkCategoryId" = fc.id
FROM "FrameworkCategory" fc
WHERE "Control".identifier LIKE fc.identifier || '%';

Check for controls needing categorization:

❌ Verification inconclusive

🏁 Script executed:

#!/bin/bash
echo "SELECT COUNT(*) FROM \"Control\" WHERE \"frameworkCategoryId\" IS NULL;" | psql $DATABASE_URL

Length of output: 178


Manual verification required for data backfill strategy.

The shell command indicates that the psql client wasn’t available ("psql: command not found"), so we couldn’t automatically verify the existence of controls without categories. Please run the query (using a proper psql installation or equivalent tool) to verify if many existing "Control" rows have a NULL "frameworkCategoryId". If so, consider implementing a data backfill plan as suggested.

packages/db/prisma/migrations/20250210204644_add_new_requirement_types/migration.sql (1)

1-12: Ensure PostgreSQL version compatibility and validate existing data.

The migration adds multiple enum values, which requires PostgreSQL 12+. For broader compatibility:

Check PostgreSQL version and existing data:

For PostgreSQL 11 and earlier, split into separate migrations:

-- 20250210204644_add_procedure_type.sql
ALTER TYPE "RequirementType" ADD VALUE 'procedure';

-- 20250210204645_add_evidence_type.sql
ALTER TYPE "RequirementType" ADD VALUE 'evidence';

-- 20250210204646_add_training_type.sql
ALTER TYPE "RequirementType" ADD VALUE 'training';
❌ Verification inconclusive

🏁 Script executed:

#!/bin/bash
# Check PostgreSQL version
echo "SELECT version();" | psql $DATABASE_URL

# Check if any requirements already use similar values
echo "SELECT DISTINCT requirement_type FROM \"ControlRequirement\";" | psql $DATABASE_URL

Length of output: 299


Manual Verification: PostgreSQL Version & Data Validation Required

The psql command did not run because it wasn’t found, so we couldn’t auto-verify the PostgreSQL version or the state of existing data. Please manually confirm that the PostgreSQL version in use is 12 or newer and that the ControlRequirement table doesn’t contain conflicting values before applying the migration.

packages/db/prisma/schema.prisma (2)

842-851: Consider defining a schema for the usedBy field.

The usedBy field in the Policy model is of type Json but lacks a schema definition. This could lead to inconsistent data across policies.

Consider one of these approaches:

  1. Define a schema using Zod or similar validation library
  2. Create a separate model to track policy usage with proper relationships

Example using Zod (create a new file policy.schema.ts):

import { z } from 'zod';

export const PolicyUsageSchema = z.object({
  organizations: z.array(z.string()),
  lastUsed: z.string().datetime().optional(),
  // Add other relevant fields
});

export type PolicyUsage = z.infer<typeof PolicyUsageSchema>;

256-273: Add documentation for the domain field.

The purpose of the optional domain field in the Control model is unclear. Consider adding documentation to explain its intended use.

Add documentation comments above the field:

  id          String  @id @default(cuid())
  code        String  @unique
  name        String
  description String?
+ /// Domain represents the high-level area or category this control belongs to
+ /// Example: "Access Control", "Data Protection", etc.
  domain      String?
packages/db/prisma/seedTypes.js (1)

1-2: Remove redundant "use strict" directive.

JavaScript modules are automatically in strict mode, making the "use strict" directive redundant.

Apply this diff to remove the redundant directive:

-"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 463a6c2 and f7df90e.

⛔ Files ignored due to path filters (1)
  • apps/app/languine.lock is excluded by !**/*.lock
📒 Files selected for processing (53)
  • apps/app/src/actions/framework/get-frameworks-action.ts (1 hunks)
  • apps/app/src/actions/framework/select-frameworks-action.ts (1 hunks)
  • apps/app/src/actions/organization/create-organization-action.ts (1 hunks)
  • apps/app/src/actions/soc2-seed.ts (1 hunks)
  • apps/app/src/actions/types.ts (2 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/(home)/Components/FrameworkProgress.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/(home)/Components/FrameworksGrid.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/(home)/Components/FrameworksOverview.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/(home)/hooks/useFrameworks.ts (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/(home)/layout.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/(home)/page.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/policies/(overview)/Actions/get-policies.ts (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/policies/(overview)/Components/PoliciesOverview.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/policies/(overview)/hooks/usePolicies.ts (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/policies/(overview)/loading.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/policies/(overview)/page.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/policies/all/Actions/get-policies.ts (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/policies/all/Components/PoliciesTable.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/policies/all/hooks/usePolicies.ts (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/policies/all/layout.tsx (1 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/policies/all/page.tsx (1 hunks)
  • apps/app/src/auth/config.ts (0 hunks)
  • apps/app/src/auth/org.ts (0 hunks)
  • apps/app/src/components/charts/framework-progress.tsx (0 hunks)
  • apps/app/src/components/policies/charts/policies-by-framework.tsx (3 hunks)
  • apps/app/src/components/tables/policies/columns.tsx (5 hunks)
  • apps/app/src/components/tables/policies/data-table.tsx (1 hunks)
  • apps/app/src/locales/en.ts (1 hunks)
  • apps/app/src/locales/es.ts (1 hunks)
  • apps/app/src/locales/fr.ts (1 hunks)
  • apps/app/src/locales/no.ts (1 hunks)
  • apps/app/src/locales/pt.ts (1 hunks)
  • packages/data/categories/soc2.json (1 hunks)
  • packages/data/controls/soc2.json (1 hunks)
  • packages/data/frameworks.json (1 hunks)
  • packages/data/policies/password_policy.json (1 hunks)
  • packages/db/package.json (1 hunks)
  • packages/db/prisma/migrations/20250206222242_add_policies_tables/migration.sql (1 hunks)
  • packages/db/prisma/migrations/20250206224138_add_controls_to_policies/migration.sql (1 hunks)
  • packages/db/prisma/migrations/20250206230606_add_requirements_to_controls/migration.sql (1 hunks)
  • packages/db/prisma/migrations/20250206231115_simplify_controls_table/migration.sql (1 hunks)
  • packages/db/prisma/migrations/20250207011314_add_policy_id/migration.sql (1 hunks)
  • packages/db/prisma/migrations/20250210204644_add_new_requirement_types/migration.sql (1 hunks)
  • packages/db/prisma/migrations/20250210212236_connect_category_to_controls/migration.sql (1 hunks)
  • packages/db/prisma/migrations/20250210214327_add_usedBy_to_policies/migration.sql (1 hunks)
  • packages/db/prisma/migrations/20250210222417_change_used_by_datatype/migration.sql (1 hunks)
  • packages/db/prisma/migrations/20250211190006_add_organization_policy_join_table/migration.sql (1 hunks)
  • packages/db/prisma/migrations/migration_lock.toml (1 hunks)
  • packages/db/prisma/schema.prisma (5 hunks)
  • packages/db/prisma/seed.js (1 hunks)
  • packages/db/prisma/seed.ts (1 hunks)
  • packages/db/prisma/seedTypes.js (1 hunks)
  • packages/db/prisma/seedTypes.ts (1 hunks)
💤 Files with no reviewable changes (3)
  • apps/app/src/auth/org.ts
  • apps/app/src/auth/config.ts
  • apps/app/src/components/charts/framework-progress.tsx
✅ Files skipped from review due to trivial changes (5)
  • apps/app/src/app/[locale]/(app)/(dashboard)/(home)/layout.tsx
  • packages/db/prisma/migrations/migration_lock.toml
  • packages/data/frameworks.json
  • apps/app/src/components/tables/policies/data-table.tsx
  • packages/data/categories/soc2.json
🧰 Additional context used
🪛 Biome (1.9.4)
packages/db/prisma/seed.js

[error] 1-1: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)


[error] 3-3: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 2-4: This function expression can be turned into an arrow function.

Function expressions that don't use this can be turned into arrow functions.
Safe fix: Use an arrow function instead.

(lint/complexity/useArrowFunction)


[error] 40-40: The comma operator is disallowed.

Its use is often confusing and obscures side effects.

(lint/style/noCommaOperator)


[error] 41-41: The comma operator is disallowed.

Its use is often confusing and obscures side effects.

(lint/style/noCommaOperator)


[error] 45-45: The comma operator is disallowed.

Its use is often confusing and obscures side effects.

(lint/style/noCommaOperator)


[error] 45-45: The comma operator is disallowed.

Its use is often confusing and obscures side effects.

(lint/style/noCommaOperator)


[error] 67-67: The comma operator is disallowed.

Its use is often confusing and obscures side effects.

(lint/style/noCommaOperator)


[error] 68-68: The comma operator is disallowed.

Its use is often confusing and obscures side effects.

(lint/style/noCommaOperator)


[error] 95-95: The comma operator is disallowed.

Its use is often confusing and obscures side effects.

(lint/style/noCommaOperator)


[error] 129-129: The comma operator is disallowed.

Its use is often confusing and obscures side effects.

(lint/style/noCommaOperator)


[error] 1-4: Use let or const instead of var.

A variable declared with var is accessible in the whole module. Thus, the variable can be accessed before its initialization and outside the block where it is declared.
See MDN web docs for more details.
Unsafe fix: Use 'const' instead.

(lint/style/noVar)

packages/db/prisma/seedTypes.js

[error] 1-1: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)

🔇 Additional comments (31)
apps/app/src/app/[locale]/(app)/(dashboard)/(home)/page.tsx (2)

1-1: No issues with the new import.
Everything looks good here.


3-4: Verify removal of user session checks.
Previously, this page might have included logic safeguarding access by requiring a valid session or organization. By returning <FrameworksOverview /> unconditionally, this route could become unguarded if that session-based check is now missing elsewhere.

apps/app/src/app/[locale]/(app)/(dashboard)/policies/(overview)/page.tsx (1)

3-12: Streamlined approach to rendering policies overview.
Replacing the old data-fetch logic with a single <PoliciesOverview /> component clarifies the flow. The session check is still present, ensuring appropriate redirection if organizationId is missing. No issues found.

apps/app/src/app/[locale]/(app)/(dashboard)/policies/all/page.tsx (4)

5-7: Proactive adoption of Suspense and Skeleton.
Using React's Suspense and a fallback Skeleton for loading indicators is a good practice that provides a better user experience.


9-9: Search and filter removal verification.
Previously, this page may have supported searching or filtering via props. Now, the function signature no longer takes searchParams. Verify if the removal of these features is intentional or if you plan to reimplement them.


16-31: Confirm Artifact: { some: {} } usage.
The condition 'Artifact: { some: {} }' in the db.user.findMany call is valid syntactically but has no filters inside some. Please verify if this empty filter is intended or if a more specific condition is needed.


34-42: Cleanly handled fallback UI.
Providing a skeleton fallback while the PoliciesTable loads is a user-friendly design choice.

packages/db/prisma/seedTypes.ts (1)

1-25: Everything looks good!

The interfaces are clear, concise, and straightforward. No issues found.

apps/app/src/app/[locale]/(app)/(dashboard)/policies/all/layout.tsx (1)

13-13: LGTM!

The added styling classes improve layout consistency by centering content and adding proper spacing.

apps/app/src/app/[locale]/(app)/(dashboard)/policies/(overview)/hooks/usePolicies.ts (1)

19-35: LGTM!

The hook implementation follows React best practices and properly handles loading, error, and data states.

apps/app/src/app/[locale]/(app)/(dashboard)/policies/(overview)/Components/PoliciesOverview.tsx (1)

36-37: Complete the implementation of commented components.

The commented components PolicyOverview and PoliciesByAssignee suggest incomplete implementation. Consider implementing these components to provide a complete overview of policies.

Please confirm if these components are intentionally commented out or if they need to be implemented.

Also applies to: 40-42

apps/app/src/app/[locale]/(app)/(dashboard)/(home)/Components/FrameworkProgress.tsx (2)

15-20: LGTM! Well-structured type definition.

The Props interface is well-defined with clear types and proper nesting of the framework relationships.


49-54: Verify the progress calculation logic.

The progress calculation assumes all controls should be "compliant". Verify if this is the correct status to check for, as there might be other valid statuses.

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Search for other potential status values in the codebase
rg -i "status.*=.*['\"].*['\"]" --type ts --type tsx -A 2 -B 2

Length of output: 96


Let's re-run the search with an updated shell script that explicitly targets both .ts and .tsx files:

This should help us manually verify if there are any other status values in use throughout the codebase beyond "compliant".


🏁 Script executed:

#!/bin/bash
rg -i "status\s*[:=]\s*['\"][^'\"]+['\"]" -g "*.tsx" -g "*.ts" -A 2 -B 2

Length of output: 27617


Progress Calculation Verification:
After searching the codebase, the only status used to indicate completion within the context of organization controls appears to be "compliant". Other status strings (e.g., "draft", "not_started", "open", etc.) are used in different contexts (policies, risks, tasks, etc.) and do not seem to apply to the controls in the framework progress calculation. Based on the gathered evidence, the current logic that counts only controls with a status of "compliant" for progress calculation appears to be correct.

apps/app/src/components/tables/policies/columns.tsx (1)

15-17: LGTM! Clear type extension.

The PolicyType interface properly extends OrganizationPolicy and includes the necessary policy relationship.

apps/app/src/actions/types.ts (1)

7-15: LGTM! Well-designed discriminated union type.

The ActionData type effectively uses TypeScript's discriminated unions to ensure mutually exclusive data and error states.

apps/app/src/locales/en.ts (1)

564-595: LGTM! Well-structured localization strings.

The new framework management UI strings are well-organized and follow the existing localization patterns.

apps/app/src/locales/no.ts (1)

506-534: LGTM! Accurate Norwegian translations.

The Norwegian translations for framework management UI are accurate and maintain consistency with the English strings.

apps/app/src/locales/pt.ts (1)

506-534: LGTM! Accurate Portuguese translations.

The Portuguese translations for framework management UI are accurate and maintain consistency with the English strings.

apps/app/src/locales/es.ts (1)

505-533: LGTM! The translations are well-structured and comprehensive.

The new translations for the frameworks section provide clear and consistent Spanish translations that maintain a professional tone while being user-friendly. The translations cover all necessary UI states including loading, error, empty states, and user actions.

apps/app/src/locales/fr.ts (1)

505-533: LGTM! The French translations are accurate and well-adapted.

The new translations maintain consistency with other language files while properly adapting the content for French users. The translations are grammatically correct and maintain a professional yet approachable tone.

packages/db/prisma/migrations/20250206224138_add_controls_to_policies/migration.sql (1)

1-3: Verify the data type choice for the controls column.

While TEXT[] can store multiple controls, consider if JSONB might be more appropriate for storing structured control data, especially since a later migration changes a similar column (usedBy) to JSONB. This would provide more flexibility in storing metadata about each control.

❌ Verification inconclusive

🏁 Script executed:

#!/bin/bash
# Check if controls might contain structured data
rg -A 5 '"controls":\s*{' 

Length of output: 121


Data Type Verification for "controls" Column

The migration file shows that the "controls" column is added as a TEXT[] array. Our initial check (using the provided regex) produced an error due to an unescaped curly brace and—once corrected—yields no evidence in the codebase that structured (JSON-like) data is being stored in this field. In contrast, a later migration alters a similar column (“usedBy”) to JSONB, suggesting that structured data might be expected in that context.

Please verify manually that the "controls" field isn’t destined to hold more complex, structured metadata. If structured control data is anticipated, revisiting the data type choice (switching to JSONB) would be advisable.

packages/db/prisma/migrations/20250210214327_add_usedBy_to_policies/migration.sql (1)

1-6: Consider implementing a data migration strategy.

The warning indicates that all data in the controls column will be lost. Consider implementing a data migration strategy to preserve any existing control data before dropping the column.

❌ Verification inconclusive

🏁 Script executed:

#!/bin/bash
# Check if there's any existing data in the controls column
rg -A 5 'INSERT.*"controls"' 

Length of output: 30


Action Required: Verify Data Preservation Strategy

The initial search did not reveal any explicit data migration (e.g., INSERT/UPDATE queries) that copies data from the controls column before it’s dropped. This suggests that a specific migration step to preserve existing data may be missing, but further manual review is recommended to determine if the column’s data is used elsewhere or if an alternative backup exists.

Please manually verify if:

  • The controls column is populated in production.
  • There’s an alternate or implicit migration strategy that preserves its data (e.g., in application logic or within other migration scripts).
  • Dropping this column without backup could have unintended consequences.
packages/data/policies/password_policy.json (1)

7-11: Verify SOC2 control mappings.

The policy references SOC2 controls CC1.1-3. Please ensure these control mappings are accurate and the referenced controls exist in the framework definition.

❌ Verification inconclusive

🏁 Script executed:

#!/bin/bash
# Check if referenced controls exist in SOC2 framework definition
jq -e '.CC1 | contains(["CC1.1", "CC1.2", "CC1.3"])' packages/data/categories/soc2.json

Length of output: 232


SOC2 Framework Structure Mismatch – Manual Verification Needed

The initial jq command failed because .CC1 is an object rather than an array. It appears that the list of controls may be nested (for example, under a property like controls). To confirm the mapping, please verify the structure of the SOC2 framework definition as follows:

  • Use a command like jq '.CC1' packages/data/categories/soc2.json to inspect the object.
  • Check if there is a nested array—e.g., .CC1.controls—that actually contains the control identifiers.
  • If the controls are found under a nested property (such as .CC1.controls), update the jq validation to check that array (e.g., jq -e '.CC1.controls | contains(["CC1.1", "CC1.2", "CC1.3"])' packages/data/categories/soc2.json).

Please manually verify the file’s structure and adjust the check accordingly.

packages/db/package.json (1)

31-33: LGTM!

The Prisma seed configuration is correctly set up to use ts-node for running the seed script.

packages/db/prisma/migrations/20250211190006_add_organization_policy_join_table/migration.sql (1)

1-30: LGTM!

The migration is well-structured with:

  • Appropriate enum values for policy lifecycle management
  • Proper table structure with timestamps
  • Necessary indexes for query optimization
  • Correct foreign key constraints with cascade actions
packages/db/prisma/migrations/20250206230606_add_requirements_to_controls/migration.sql (2)

1-3: Inconsistency detected in the AI summary.

The AI summary lists additional enum values ('procedure', 'evidence', 'training') that are not present in the actual migration. The actual RequirementType enum only includes 'policy', 'file', and 'link'.

Likely an incorrect or invalid review comment.


4-48: LGTM!

The migration is well-structured with:

  • Clear table definitions
  • Appropriate constraints and indexes
  • Proper foreign key relationships with cascade actions
packages/db/prisma/schema.prisma (1)

813-838: LGTM!

The Employee model changes are well-structured with proper relationships, indexes, and constraints. The implementation follows best practices.

packages/db/prisma/migrations/20250206222242_add_policies_tables/migration.sql (3)

4-57: LGTM!

The enum definitions are well-structured with clear naming conventions and comprehensive values that cover all necessary states and types.


58-1025: LGTM!

The table definitions and relationships are well-structured with:

  • Proper primary key constraints
  • Appropriate foreign key relationships with correct ON DELETE actions
  • Useful indexes for performance optimization
  • Consistent naming conventions

1-2: Verify PostgreSQL vector extension availability.

The migration assumes the PostgreSQL vector extension is available. This might not be true for all PostgreSQL installations.

Before deploying, ensure that:

  1. The PostgreSQL version supports the vector extension
  2. The extension is installed on the database server
  3. The database user has permissions to create extensions

Run this script to check the extension availability:

❌ Verification inconclusive

🏁 Script executed:

#!/bin/bash
# Description: Check if the vector extension is available and can be created

# Test: Connect to the database and list available extensions
PGPASSWORD=$DATABASE_PASSWORD psql -h $DATABASE_HOST -U $DATABASE_USER -d $DATABASE_NAME -c "\dx vector"

# Test: Check if the current user can create extensions
PGPASSWORD=$DATABASE_PASSWORD psql -h $DATABASE_HOST -U $DATABASE_USER -d $DATABASE_NAME -c "SELECT current_user, has_database_privilege(current_user, 'CREATE') as can_create;"

Length of output: 359


Action: Manually Verify PostgreSQL Extension Availability

The script output indicates that the psql command was not found in the current environment, so we couldn't verify if the vector extension is available. Please run the test in an environment where psql is installed and operational. Confirm that:

  • The PostgreSQL version in use supports the vector extension.
  • The extension is present or can be installed on the database server.
  • The current database user has the privileges needed to create extensions.

Comment on lines +32 to +37
await Promise.all([
frameworkIds.map((frameworkId) =>
createOrganizationFramework(user as User, frameworkId)
),
createOrganizationPolicy(user as User, frameworkIds),
]);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix concurrency bug in Promise.all usage.

You're passing an array of promises as the first element rather than spreading them. Currently:

await Promise.all([
  frameworkIds.map((frameworkId) => createOrganizationFramework(user, frameworkId)),
  createOrganizationPolicy(user, frameworkIds),
]);

This means the first element is just an array, not a promise for each framework. Consider spreading or concatenating the mapped promises. For example:

- await Promise.all([
-   frameworkIds.map((frameworkId) =>
-     createOrganizationFramework(user, frameworkId)
-   ),
-   createOrganizationPolicy(user, frameworkIds),
- ]);
+ await Promise.all(
+   frameworkIds.map((frameworkId) =>
+     createOrganizationFramework(user, frameworkId)
+   ).concat(
+     createOrganizationPolicy(user, frameworkIds)
+   )
+ );

import { db, type OrganizationPolicy } from "@bubba/db";
import { z } from "zod";

const schema = z.object({});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add input validation schema.

The schema is currently empty. Consider adding validation for potential query parameters like pagination, sorting, or filtering.

-const schema = z.object({});
+const schema = z.object({
+  page: z.number().optional(),
+  limit: z.number().optional(),
+  sort: z.enum(['asc', 'desc']).optional(),
+});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const schema = z.object({});
const schema = z.object({
page: z.number().optional(),
limit: z.number().optional(),
sort: z.enum(['asc', 'desc']).optional(),
});

Comment on lines +34 to +42
try {
const policies = await db.organizationPolicy.findMany({
where: {
organizationId: user.organizationId!,
},
include: {
policy: true,
},
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove non-null assertion and handle type safely.

The non-null assertion on organizationId is unsafe. The type has already been checked above.

 try {
   const policies = await db.organizationPolicy.findMany({
     where: {
-      organizationId: user.organizationId!,
+      organizationId: user.organizationId,
     },
     include: {
       policy: true,
     },
   });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
const policies = await db.organizationPolicy.findMany({
where: {
organizationId: user.organizationId!,
},
include: {
policy: true,
},
});
try {
const policies = await db.organizationPolicy.findMany({
where: {
organizationId: user.organizationId,
},
include: {
policy: true,
},
});

Comment on lines +7 to +10
const schema = z.object({
page: z.number().default(1),
perPage: z.number().default(10),
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add input validation for pagination parameters.

The schema should validate that page and perPage are positive numbers to prevent invalid database queries.

 const schema = z.object({
-  page: z.number().default(1),
-  perPage: z.number().default(10),
+  page: z.number().min(1, "Page must be greater than 0").default(1),
+  perPage: z.number().min(1, "Items per page must be greater than 0").max(100, "Maximum 100 items per page").default(10),
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const schema = z.object({
page: z.number().default(1),
perPage: z.number().default(10),
});
const schema = z.object({
page: z.number().min(1, "Page must be greater than 0").default(1),
perPage: z.number().min(1, "Items per page must be greater than 0").max(100, "Maximum 100 items per page").default(10),
});

Comment on lines +14 to 17
export function PoliciesByFramework() {
const t = useI18n();

console.log("Chart Data:", data);
const { data } = usePolicies();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add loading and error state handling.

The component should handle loading and error states from the usePolicies hook.

 export function PoliciesByFramework() {
   const t = useI18n();
-  const { data } = usePolicies();
+  const { data, isLoading, error } = usePolicies();
+
+  if (error) {
+    return (
+      <Card>
+        <CardHeader>
+          <CardTitle>{t("policies.dashboard.policies_by_framework")}</CardTitle>
+        </CardHeader>
+        <CardContent className="flex h-[300px] items-center justify-center text-destructive">
+          {t("common.error.loading")}
+        </CardContent>
+      </Card>
+    );
+  }
+
+  if (isLoading) {
+    return (
+      <Card>
+        <CardHeader>
+          <CardTitle>{t("policies.dashboard.policies_by_framework")}</CardTitle>
+        </CardHeader>
+        <CardContent className="flex h-[300px] items-center justify-center">
+          <Spinner />
+        </CardContent>
+      </Card>
+    );
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function PoliciesByFramework() {
const t = useI18n();
console.log("Chart Data:", data);
const { data } = usePolicies();
export function PoliciesByFramework() {
const t = useI18n();
const { data, isLoading, error } = usePolicies();
if (error) {
return (
<Card>
<CardHeader>
<CardTitle>{t("policies.dashboard.policies_by_framework")}</CardTitle>
</CardHeader>
<CardContent className="flex h-[300px] items-center justify-center text-destructive">
{t("common.error.loading")}
</CardContent>
</Card>
);
}
if (isLoading) {
return (
<Card>
<CardHeader>
<CardTitle>{t("policies.dashboard.policies_by_framework")}</CardTitle>
</CardHeader>
<CardContent className="flex h-[300px] items-center justify-center">
<Spinner />
</CardContent>
</Card>
);
}
// TODO: Render the component using `data`
}

Comment on lines +237 to +287
async function seedPolicyFramework() {
const policies = await prisma.policy.findMany();
console.log(
`🔄 Processing ${policies.length} policies for framework mapping`
);

for (const policy of policies) {
console.log(` ⏳ Mapping policy: ${policy.name}...`);
if (!policy.usedBy) {
console.log(` ⚠️ Policy ${policy.name} has no usedBy, skipping`);
continue;
}

for (const [frameworkId, categories] of Object.entries(
policy.usedBy as Record<string, string[]>
)) {
// Upsert the policy framework mapping.
await prisma.policyFramework.upsert({
where: { id: `${frameworkId}_${policy.id}` },
update: {
policyId: policy.id,
frameworkId: frameworkId as string,
},
create: {
id: `${frameworkId}_${policy.id}`,
policyId: policy.id,
frameworkId: frameworkId as string,
},
});

for (const [categoryCode, controlCodes] of Object.entries(categories)) {
for (const controlCode of controlCodes) {
// Upsert the policy control mapping.
await prisma.policyControl.upsert({
where: { id: `${frameworkId}_${categoryCode}_${controlCode}` },
update: {
policyId: policy.id,
controlId: controlCode,
},
create: {
id: `${frameworkId}_${categoryCode}_${controlCode}`,
policyId: policy.id,
controlId: controlCode,
},
});
}
}
}
console.log(` ✅ Policy ${policy.name} mapped`);
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling and optimize database operations.

The function should handle database errors and use batch operations for better performance.

 async function seedPolicyFramework() {
-  const policies = await prisma.policy.findMany();
+  let policies;
+  try {
+    policies = await prisma.policy.findMany();
+  } catch (error) {
+    console.error("❌ Error fetching policies:", error);
+    return;
+  }

   console.log(
     `🔄 Processing ${policies.length} policies for framework mapping`
   );

   for (const policy of policies) {
     console.log(`  ⏳ Mapping policy: ${policy.name}...`);
     if (!policy.usedBy) {
       console.log(`  ⚠️  Policy ${policy.name} has no usedBy, skipping`);
       continue;
     }

+    try {
       for (const [frameworkId, categories] of Object.entries(
         policy.usedBy as Record<string, string[]>
       )) {
         // Upsert the policy framework mapping.
         await prisma.policyFramework.upsert({
           where: { id: `${frameworkId}_${policy.id}` },
           update: {
             policyId: policy.id,
             frameworkId: frameworkId as string,
           },
           create: {
             id: `${frameworkId}_${policy.id}`,
             policyId: policy.id,
             frameworkId: frameworkId as string,
           },
         });

+        // Prepare batch operations for policy controls
+        const policyControlOperations = [];

         for (const [categoryCode, controlCodes] of Object.entries(categories)) {
           for (const controlCode of controlCodes) {
-            // Upsert the policy control mapping.
-            await prisma.policyControl.upsert({
+            policyControlOperations.push(prisma.policyControl.upsert({
               where: { id: `${frameworkId}_${categoryCode}_${controlCode}` },
               update: {
                 policyId: policy.id,
                 controlId: controlCode,
               },
               create: {
                 id: `${frameworkId}_${categoryCode}_${controlCode}`,
                 policyId: policy.id,
                 controlId: controlCode,
               },
-            });
+            }));
           }
         }

+        // Execute batch operations
+        await Promise.all(policyControlOperations);
       }
       console.log(`  ✅ Policy ${policy.name} mapped`);
+    } catch (error) {
+      console.error(`  ❌ Error mapping policy ${policy.name}:`, error);
+    }
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async function seedPolicyFramework() {
const policies = await prisma.policy.findMany();
console.log(
`🔄 Processing ${policies.length} policies for framework mapping`
);
for (const policy of policies) {
console.log(` ⏳ Mapping policy: ${policy.name}...`);
if (!policy.usedBy) {
console.log(` ⚠️ Policy ${policy.name} has no usedBy, skipping`);
continue;
}
for (const [frameworkId, categories] of Object.entries(
policy.usedBy as Record<string, string[]>
)) {
// Upsert the policy framework mapping.
await prisma.policyFramework.upsert({
where: { id: `${frameworkId}_${policy.id}` },
update: {
policyId: policy.id,
frameworkId: frameworkId as string,
},
create: {
id: `${frameworkId}_${policy.id}`,
policyId: policy.id,
frameworkId: frameworkId as string,
},
});
for (const [categoryCode, controlCodes] of Object.entries(categories)) {
for (const controlCode of controlCodes) {
// Upsert the policy control mapping.
await prisma.policyControl.upsert({
where: { id: `${frameworkId}_${categoryCode}_${controlCode}` },
update: {
policyId: policy.id,
controlId: controlCode,
},
create: {
id: `${frameworkId}_${categoryCode}_${controlCode}`,
policyId: policy.id,
controlId: controlCode,
},
});
}
}
}
console.log(` ✅ Policy ${policy.name} mapped`);
}
}
async function seedPolicyFramework() {
let policies;
try {
policies = await prisma.policy.findMany();
} catch (error) {
console.error("❌ Error fetching policies:", error);
return;
}
console.log(
`🔄 Processing ${policies.length} policies for framework mapping`
);
for (const policy of policies) {
console.log(` ⏳ Mapping policy: ${policy.name}...`);
if (!policy.usedBy) {
console.log(` ⚠️ Policy ${policy.name} has no usedBy, skipping`);
continue;
}
try {
for (const [frameworkId, categories] of Object.entries(
policy.usedBy as Record<string, string[]>
)) {
// Upsert the policy framework mapping.
await prisma.policyFramework.upsert({
where: { id: `${frameworkId}_${policy.id}` },
update: {
policyId: policy.id,
frameworkId: frameworkId as string,
},
create: {
id: `${frameworkId}_${policy.id}`,
policyId: policy.id,
frameworkId: frameworkId as string,
},
});
// Prepare batch operations for policy controls
const policyControlOperations = [];
for (const [categoryCode, controlCodes] of Object.entries(categories)) {
for (const controlCode of controlCodes) {
policyControlOperations.push(
prisma.policyControl.upsert({
where: { id: `${frameworkId}_${categoryCode}_${controlCode}` },
update: {
policyId: policy.id,
controlId: controlCode,
},
create: {
id: `${frameworkId}_${categoryCode}_${controlCode}`,
policyId: policy.id,
controlId: controlCode,
},
})
);
}
}
// Execute batch operations
await Promise.all(policyControlOperations);
}
console.log(` ✅ Policy ${policy.name} mapped`);
} catch (error) {
console.error(` ❌ Error mapping policy ${policy.name}:`, error);
}
}
}

Comment on lines +78 to +110
async function seedFrameworks() {
const frameworksFile = join(__dirname, "../../data/frameworks.json");
const frameworksJson = JSON.parse(
readFileSync(frameworksFile, "utf8")
) as Record<string, Framework>;

console.log(
`🔍 Found ${Object.keys(frameworksJson).length} frameworks to process`
);

// Populate the app level frameworks that every org has access to.
for (const [frameworkId, frameworkData] of Object.entries(frameworksJson)) {
console.log(` ⏳ Processing framework: ${frameworkData.name}...`);
// First, upsert the framework itself.
const insertedFramework = await prisma.framework.upsert({
where: { id: frameworkId },
update: {
description: frameworkData.description,
version: frameworkData.version,
},
create: {
id: frameworkId,
name: frameworkData.name,
description: frameworkData.description,
version: frameworkData.version,
},
});

// Then, upsert the framework categories.
await seedFrameworkCategories(insertedFramework.id);
console.log(` ✅ Framework ${frameworkData.name} processed`);
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for framework file operations and data validation.

The function should handle potential file read errors and validate framework data before upserting.

 async function seedFrameworks() {
   const frameworksFile = join(__dirname, "../../data/frameworks.json");
+  if (!fs.existsSync(frameworksFile)) {
+    console.error(`❌ Frameworks file not found at ${frameworksFile}`);
+    return;
+  }

+  let frameworksJson;
+  try {
-    const frameworksJson = JSON.parse(
+    frameworksJson = JSON.parse(
       readFileSync(frameworksFile, "utf8")
     ) as Record<string, Framework>;
+  } catch (error) {
+    console.error("❌ Error reading frameworks file:", error);
+    return;
+  }

   console.log(
     `🔍 Found ${Object.keys(frameworksJson).length} frameworks to process`
   );

   // Populate the app level frameworks that every org has access to.
   for (const [frameworkId, frameworkData] of Object.entries(frameworksJson)) {
+    // Validate required fields
+    if (!frameworkData.name || !frameworkData.version) {
+      console.error(`❌ Missing required fields for framework ${frameworkId}`);
+      continue;
+    }

     console.log(`  ⏳ Processing framework: ${frameworkData.name}...`);
     try {
       // First, upsert the framework itself.
       const insertedFramework = await prisma.framework.upsert({
         where: { id: frameworkId },
         update: {
           description: frameworkData.description,
           version: frameworkData.version,
         },
         create: {
           id: frameworkId,
           name: frameworkData.name,
           description: frameworkData.description,
           version: frameworkData.version,
         },
       });

       // Then, upsert the framework categories.
       await seedFrameworkCategories(insertedFramework.id);
       console.log(`  ✅ Framework ${frameworkData.name} processed`);
+    } catch (error) {
+      console.error(`  ❌ Error processing framework ${frameworkData.name}:`, error);
+    }
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async function seedFrameworks() {
const frameworksFile = join(__dirname, "../../data/frameworks.json");
const frameworksJson = JSON.parse(
readFileSync(frameworksFile, "utf8")
) as Record<string, Framework>;
console.log(
`🔍 Found ${Object.keys(frameworksJson).length} frameworks to process`
);
// Populate the app level frameworks that every org has access to.
for (const [frameworkId, frameworkData] of Object.entries(frameworksJson)) {
console.log(` ⏳ Processing framework: ${frameworkData.name}...`);
// First, upsert the framework itself.
const insertedFramework = await prisma.framework.upsert({
where: { id: frameworkId },
update: {
description: frameworkData.description,
version: frameworkData.version,
},
create: {
id: frameworkId,
name: frameworkData.name,
description: frameworkData.description,
version: frameworkData.version,
},
});
// Then, upsert the framework categories.
await seedFrameworkCategories(insertedFramework.id);
console.log(` ✅ Framework ${frameworkData.name} processed`);
}
}
async function seedFrameworks() {
const frameworksFile = join(__dirname, "../../data/frameworks.json");
if (!fs.existsSync(frameworksFile)) {
console.error(`❌ Frameworks file not found at ${frameworksFile}`);
return;
}
let frameworksJson;
try {
frameworksJson = JSON.parse(
readFileSync(frameworksFile, "utf8")
) as Record<string, Framework>;
} catch (error) {
console.error("❌ Error reading frameworks file:", error);
return;
}
console.log(
`🔍 Found ${Object.keys(frameworksJson).length} frameworks to process`
);
// Populate the app level frameworks that every org has access to.
for (const [frameworkId, frameworkData] of Object.entries(frameworksJson)) {
// Validate required fields
if (!frameworkData.name || !frameworkData.version) {
console.error(`❌ Missing required fields for framework ${frameworkId}`);
continue;
}
console.log(` ⏳ Processing framework: ${frameworkData.name}...`);
try {
// First, upsert the framework itself.
const insertedFramework = await prisma.framework.upsert({
where: { id: frameworkId },
update: {
description: frameworkData.description,
version: frameworkData.version,
},
create: {
id: frameworkId,
name: frameworkData.name,
description: frameworkData.description,
version: frameworkData.version,
},
});
// Then, upsert the framework categories.
await seedFrameworkCategories(insertedFramework.id);
console.log(` ✅ Framework ${frameworkData.name} processed`);
} catch (error) {
console.error(` ❌ Error processing framework ${frameworkData.name}:`, error);
}
}
}

Comment on lines +7 to +9
-- AlterTable
ALTER TABLE "Policy" DROP COLUMN "controls",
ADD COLUMN "usedBy" TEXT[];
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify the data type choice for the usedBy column.

Similar to the previous migration, consider if JSONB would be more appropriate than TEXT[] for the usedBy column, especially if it needs to store metadata about usage. This would provide more flexibility in storing structured data about policy usage.


🏁 Script executed:

#!/bin/bash
# Check if usedBy might contain structured data
rg -A 5 '"usedBy":\s*{' 

Length of output: 115


Below is a shell script using a corrected regex (escaping the curly brace) along with an extra search for any JSON‐related usage of “usedBy”. This should help verify whether there’s any evidence in the codebase that the column is meant to store structured JSON data.

Action Required:
Please run the above commands to check if anywhere in the repository there is code indicating that the “usedBy” field is handled as structured JSON data. If no such evidence is found and the migration file consistently defines “usedBy” as a TEXT array, then the current data type choice appears correct.


🏁 Script executed:

[run_scripts]
#!/bin/bash
echo "Searching for JSON-like usage of 'usedBy':"
rg -n '"usedBy":\s*\{' .
echo "Looking for any reference combining 'usedBy' with JSON in the code:"
rg -n 'usedBy.*json' .
echo "Displaying the migration snippet to confirm the column definition:"
sed -n '1,20p' packages/db/prisma/migrations/20250210214327_add_usedBy_to_policies/migration.sql

Length of output: 950


Data Type Mismatch: Consider Using JSONB for "usedBy"

  • The search output from packages/data/policies/password_policy.json shows a JSON object for "usedBy", implying it is intended to hold structured data.
  • The migration, however, defines "usedBy" as a TEXT[], which is less flexible for storing metadata compared to JSONB.
  • Please review how the application uses this field; if structured metadata is expected, switching to a JSONB column type is likely more appropriate.

"slug": "password-policy",
"name": "Password Policy",
"description": "This policy outlines the requirements for passwords used by employees.",
"template": "<html>...</html>",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add actual template content instead of placeholder.

The template field contains a placeholder <html>...</html> instead of actual policy content.

Replace with proper HTML template content for the password policy.

Comment on lines +1 to +807
{
"CC1.1": {
"code": "CC1.1",
"name": "Board Oversight",
"description": "The board of directors demonstrates independence from management and exercises oversight of the development and performance of internal control.",
"domain": "Control Environment",
"categoryId": "CC1",
"requirements": [
{
"id": "CC1.1-policy",
"type": "policy",
"description": "Board oversight and governance policy",
"policyId": "password_policy"
},
{
"id": "CC1.1-procedure",
"type": "procedure",
"description": "Board oversight procedures"
}
]
},
"CC1.2": {
"code": "CC1.2",
"name": "Management Philosophy",
"description": "Management establishes, with board oversight, structures, reporting lines, and appropriate authorities and responsibilities in the pursuit of objectives.",
"domain": "Control Environment",
"categoryId": "CC1",
"requirements": [
{
"id": "CC1.2-policy",
"type": "policy",
"description": "Organizational structure and management policy",
"policyId": "password_policy"
},
{
"id": "CC1.2-procedure",
"type": "procedure",
"description": "Management oversight procedures"
}
]
},
"CC1.3": {
"code": "CC1.3",
"name": "Organizational Structure",
"description": "The organization demonstrates a commitment to attract, develop, and retain competent individuals in alignment with objectives.",
"domain": "Control Environment",
"categoryId": "CC1",
"requirements": [
{
"id": "CC1.3-policy",
"type": "policy",
"description": "Personnel management policy",
"policyId": "password_policy"
},
{
"id": "CC1.3-procedure",
"type": "procedure",
"description": "Personnel management procedures"
}
]
},
"CC1.4": {
"code": "CC1.4",
"name": "Personnel Policies",
"description": "The organization holds individuals accountable for their internal control responsibilities in the pursuit of objectives.",
"domain": "Control Environment",
"categoryId": "CC1",
"requirements": [
{
"id": "CC1.4-policy",
"type": "policy",
"description": "Personnel accountability policy",
"policyId": "password_policy"
},
{
"id": "CC1.4-procedure",
"type": "procedure",
"description": "Personnel accountability procedures"
},
{
"id": "CC1.4-training",
"type": "training",
"description": "Personnel accountability training"
}
]
},
"CC1.5": {
"code": "CC1.5",
"name": "Code of Conduct",
"description": "The organization demonstrates a commitment to integrity and ethical values.",
"domain": "Control Environment",
"categoryId": "CC1",
"requirements": [
{
"id": "CC1.5-policy",
"type": "policy",
"description": "Code of conduct policy",
"policyId": "password_policy"
},
{
"id": "CC1.5-training",
"type": "training",
"description": "Ethics and integrity training"
}
]
},
"CC2.1": {
"code": "CC2.1",
"name": "Information Quality",
"description": "The organization obtains or generates and uses relevant, quality information to support the functioning of internal control.",
"domain": "Communication",
"categoryId": "CC2",
"requirements": [
{
"id": "CC2.1-policy",
"type": "policy",
"description": "Information quality policy",
"policyId": "password_policy"
},
{
"id": "CC2.1-procedure",
"type": "procedure",
"description": "Information management procedures"
},
{
"id": "CC2.1-evidence",
"type": "evidence",
"description": "Information quality metrics and reports"
}
]
},
"CC2.2": {
"code": "CC2.2",
"name": "Internal Communication",
"description": "The organization internally communicates information, including objectives and responsibilities for internal control.",
"domain": "Communication",
"categoryId": "CC2",
"requirements": [
{
"id": "CC2.2-policy",
"type": "policy",
"description": "Internal communication policy",
"policyId": "password_policy"
},
{
"id": "CC2.2-procedure",
"type": "procedure",
"description": "Communication procedures"
},
{
"id": "CC2.2-evidence",
"type": "evidence",
"description": "Internal communication records"
}
]
},
"CC2.3": {
"code": "CC2.3",
"name": "External Communication",
"description": "The organization communicates with external parties regarding matters affecting the functioning of internal control.",
"domain": "Communication",
"categoryId": "CC2",
"requirements": [
{
"id": "CC2.3-policy",
"type": "policy",
"description": "External communication policy",
"policyId": "password_policy"
},
{
"id": "CC2.3-procedure",
"type": "procedure",
"description": "External communication procedures"
},
{
"id": "CC2.3-evidence",
"type": "evidence",
"description": "External communication records"
}
]
},
"CC3.1": {
"code": "CC3.1",
"name": "Risk Assessment Process",
"description": "The organization specifies objectives with sufficient clarity to enable the identification and assessment of risks relating to objectives.",
"domain": "Risk Assessment",
"categoryId": "CC3",
"requirements": [
{
"id": "CC3.1-policy",
"type": "policy",
"description": "Risk assessment policy",
"policyId": "password_policy"
},
{
"id": "CC3.1-procedure",
"type": "procedure",
"description": "Risk assessment procedures"
},
{
"id": "CC3.1-evidence",
"type": "evidence",
"description": "Risk assessment documentation"
}
]
},
"CC3.2": {
"code": "CC3.2",
"name": "Risk Identification",
"description": "The organization 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.",
"domain": "Risk Assessment",
"categoryId": "CC3",
"requirements": [
{
"id": "CC3.2-policy",
"type": "policy",
"description": "Risk identification policy",
"policyId": "password_policy"
},
{
"id": "CC3.2-procedure",
"type": "procedure",
"description": "Risk identification procedures"
},
{
"id": "CC3.2-evidence",
"type": "evidence",
"description": "Risk register and analysis"
}
]
},
"CC3.3": {
"code": "CC3.3",
"name": "Fraud Risk Assessment",
"description": "The organization considers the potential for fraud in assessing risks to the achievement of objectives.",
"domain": "Risk Assessment",
"categoryId": "CC3",
"requirements": [
{
"id": "CC3.3-policy",
"type": "policy",
"description": "Fraud risk assessment policy",
"policyId": "password_policy"
},
{
"id": "CC3.3-procedure",
"type": "procedure",
"description": "Fraud risk assessment procedures"
},
{
"id": "CC3.3-evidence",
"type": "evidence",
"description": "Fraud risk assessment reports"
}
]
},
"CC3.4": {
"code": "CC3.4",
"name": "Change Management Risk",
"description": "The organization identifies and assesses changes that could significantly impact the system of internal control.",
"domain": "Risk Assessment",
"categoryId": "CC3",
"requirements": [
{
"id": "CC3.4-policy",
"type": "policy",
"description": "Change management risk policy",
"policyId": "password_policy"
},
{
"id": "CC3.4-procedure",
"type": "procedure",
"description": "Change impact assessment procedures"
},
{
"id": "CC3.4-evidence",
"type": "evidence",
"description": "Change impact assessments"
}
]
},
"CC4.1": {
"code": "CC4.1",
"name": "Control Monitoring",
"description": "The organization selects, develops, and performs ongoing and/or separate evaluations to ascertain whether the components of internal control are present and functioning.",
"domain": "Monitoring Activities",
"categoryId": "CC4",
"requirements": [
{
"id": "CC4.1-policy",
"type": "policy",
"description": "Control monitoring policy",
"policyId": "password_policy"
},
{
"id": "CC4.1-procedure",
"type": "procedure",
"description": "Control monitoring procedures"
},
{
"id": "CC4.1-evidence",
"type": "evidence",
"description": "Control monitoring reports"
}
]
},
"CC4.2": {
"code": "CC4.2",
"name": "Deficiency Management",
"description": "The organization evaluates and communicates internal control deficiencies in a timely manner to those parties responsible for taking corrective action.",
"domain": "Monitoring Activities",
"categoryId": "CC4",
"requirements": [
{
"id": "CC4.2-policy",
"type": "policy",
"description": "Deficiency management policy",
"policyId": "password_policy"
},
{
"id": "CC4.2-procedure",
"type": "procedure",
"description": "Deficiency management procedures"
},
{
"id": "CC4.2-evidence",
"type": "evidence",
"description": "Deficiency tracking and resolution reports"
}
]
},
"CC5.1": {
"code": "CC5.1",
"name": "Control Selection",
"description": "The organization selects and develops control activities that contribute to the mitigation of risks to the achievement of objectives to acceptable levels.",
"domain": "Control Activities",
"categoryId": "CC5",
"requirements": [
{
"id": "CC5.1-policy",
"type": "policy",
"description": "Control selection policy",
"policyId": "password_policy"
},
{
"id": "CC5.1-procedure",
"type": "procedure",
"description": "Control selection procedures"
},
{
"id": "CC5.1-evidence",
"type": "evidence",
"description": "Control selection documentation"
}
]
},
"CC5.2": {
"code": "CC5.2",
"name": "Technology Controls",
"description": "The organization selects and develops general control activities over technology to support the achievement of objectives.",
"domain": "Control Activities",
"categoryId": "CC5",
"requirements": [
{
"id": "CC5.2-policy",
"type": "policy",
"description": "Technology control policy",
"policyId": "password_policy"
},
{
"id": "CC5.2-procedure",
"type": "procedure",
"description": "Technology control procedures"
},
{
"id": "CC5.2-evidence",
"type": "evidence",
"description": "Technology control documentation"
}
]
},
"CC5.3": {
"code": "CC5.3",
"name": "Policy Implementation",
"description": "The organization deploys control activities through policies that establish what is expected and procedures that put policies into action.",
"domain": "Control Activities",
"categoryId": "CC5",
"requirements": [
{
"id": "CC5.3-policy",
"type": "policy",
"description": "Policy implementation framework",
"policyId": "password_policy"
},
{
"id": "CC5.3-procedure",
"type": "procedure",
"description": "Policy implementation procedures"
},
{
"id": "CC5.3-evidence",
"type": "evidence",
"description": "Policy implementation records"
}
]
},
"CC6.1": {
"code": "CC6.1",
"name": "Access Security",
"description": "The organization implements logical access security software, infrastructure, and architectures over protected information assets to protect them from security events.",
"domain": "Logical and Physical Access Controls",
"categoryId": "CC6",
"requirements": [
{
"id": "CC6.1-policy",
"type": "policy",
"description": "Access security policy",
"policyId": "password_policy"
},
{
"id": "CC6.1-procedure",
"type": "procedure",
"description": "Access security procedures"
},
{
"id": "CC6.1-evidence",
"type": "evidence",
"description": "Access security configurations"
}
]
},
"CC6.2": {
"code": "CC6.2",
"name": "Access Authentication",
"description": "Prior to issuing system credentials and granting system access, the organization registers and authorizes new internal and external users.",
"domain": "Logical and Physical Access Controls",
"categoryId": "CC6",
"requirements": [
{
"id": "CC6.2-policy",
"type": "policy",
"description": "User authentication policy",
"policyId": "password_policy"
},
{
"id": "CC6.2-procedure",
"type": "procedure",
"description": "User authentication procedures"
},
{
"id": "CC6.2-evidence",
"type": "evidence",
"description": "User authentication records"
}
]
},
"CC6.3": {
"code": "CC6.3",
"name": "Access Removal",
"description": "The organization removes access to protected information assets when appropriate.",
"domain": "Logical and Physical Access Controls",
"categoryId": "CC6",
"requirements": [
{
"id": "CC6.3-policy",
"type": "policy",
"description": "Access removal policy",
"policyId": "password_policy"
},
{
"id": "CC6.3-procedure",
"type": "procedure",
"description": "Access removal procedures"
},
{
"id": "CC6.3-evidence",
"type": "evidence",
"description": "Access removal records"
}
]
},
"CC6.4": {
"code": "CC6.4",
"name": "Access Review",
"description": "The organization evaluates and manages access to protected information assets on a periodic basis.",
"domain": "Logical and Physical Access Controls",
"categoryId": "CC6",
"requirements": [
{
"id": "CC6.4-policy",
"type": "policy",
"description": "Access review policy",
"policyId": "password_policy"
},
{
"id": "CC6.4-procedure",
"type": "procedure",
"description": "Access review procedures"
},
{
"id": "CC6.4-evidence",
"type": "evidence",
"description": "Access review records"
}
]
},
"CC6.5": {
"code": "CC6.5",
"name": "System Account Management",
"description": "The organization identifies and authenticates system users, devices, and other systems before allowing access.",
"domain": "Logical and Physical Access Controls",
"categoryId": "CC6",
"requirements": [
{
"id": "CC6.5-policy",
"type": "policy",
"description": "System account management policy",
"policyId": "password_policy"
},
{
"id": "CC6.5-procedure",
"type": "procedure",
"description": "System account management procedures"
},
{
"id": "CC6.5-evidence",
"type": "evidence",
"description": "System account management records"
}
]
},
"CC6.6": {
"code": "CC6.6",
"name": "Access Restrictions",
"description": "The organization restricts physical access to facilities and protected information assets.",
"domain": "Logical and Physical Access Controls",
"categoryId": "CC6",
"requirements": [
{
"id": "CC6.6-policy",
"type": "policy",
"description": "Physical access policy",
"policyId": "password_policy"
},
{
"id": "CC6.6-procedure",
"type": "procedure",
"description": "Physical access procedures"
},
{
"id": "CC6.6-evidence",
"type": "evidence",
"description": "Physical access records"
}
]
},
"CC6.7": {
"code": "CC6.7",
"name": "Information Asset Changes",
"description": "The organization manages changes to system components to minimize the risk of unauthorized changes.",
"domain": "Logical and Physical Access Controls",
"categoryId": "CC6",
"requirements": [
{
"id": "CC6.7-policy",
"type": "policy",
"description": "Change management policy",
"policyId": "password_policy"
},
{
"id": "CC6.7-procedure",
"type": "procedure",
"description": "Change management procedures"
},
{
"id": "CC6.7-evidence",
"type": "evidence",
"description": "Change management records"
}
]
},
"CC6.8": {
"code": "CC6.8",
"name": "Malicious Software Prevention",
"description": "The organization implements controls to prevent or detect and act upon the introduction of unauthorized or malicious software.",
"domain": "Logical and Physical Access Controls",
"categoryId": "CC6",
"requirements": [
{
"id": "CC6.8-policy",
"type": "policy",
"description": "Malware prevention policy",
"policyId": "password_policy"
},
{
"id": "CC6.8-procedure",
"type": "procedure",
"description": "Malware prevention procedures"
},
{
"id": "CC6.8-evidence",
"type": "evidence",
"description": "Malware prevention configurations"
}
]
},
"CC7.1": {
"code": "CC7.1",
"name": "Infrastructure Monitoring",
"description": "To detect and act upon security events in a timely manner, the organization monitors system capacity, security threats, changing regulatory requirements, and other system vulnerabilities.",
"domain": "System Operations",
"categoryId": "CC7",
"requirements": [
{
"id": "CC7.1-policy",
"type": "policy",
"description": "Infrastructure monitoring policy",
"policyId": "password_policy"
},
{
"id": "CC7.1-procedure",
"type": "procedure",
"description": "Infrastructure monitoring procedures"
},
{
"id": "CC7.1-evidence",
"type": "evidence",
"description": "Infrastructure monitoring reports"
}
]
},
"CC7.2": {
"code": "CC7.2",
"name": "Security Event Response",
"description": "The organization designs, develops, and implements policies and procedures to respond to security incidents and breaches.",
"domain": "System Operations",
"categoryId": "CC7",
"requirements": [
{
"id": "CC7.2-policy",
"type": "policy",
"description": "Incident response policy",
"policyId": "password_policy"
},
{
"id": "CC7.2-procedure",
"type": "procedure",
"description": "Incident response procedures"
},
{
"id": "CC7.2-evidence",
"type": "evidence",
"description": "Incident response records"
}
]
},
"CC7.3": {
"code": "CC7.3",
"name": "Security Event Recovery",
"description": "The organization implements recovery procedures to ensure timely restoration of systems or assets affected by security incidents.",
"domain": "System Operations",
"categoryId": "CC7",
"requirements": [
{
"id": "CC7.3-policy",
"type": "policy",
"description": "Recovery policy",
"policyId": "password_policy"
},
{
"id": "CC7.3-procedure",
"type": "procedure",
"description": "Recovery procedures"
},
{
"id": "CC7.3-evidence",
"type": "evidence",
"description": "Recovery test results"
}
]
},
"CC7.4": {
"code": "CC7.4",
"name": "Security Event Analysis",
"description": "The organization implements incident response activities to identify root causes of security incidents and develop remediation plans.",
"domain": "System Operations",
"categoryId": "CC7",
"requirements": [
{
"id": "CC7.4-policy",
"type": "policy",
"description": "Incident analysis policy",
"policyId": "password_policy"
},
{
"id": "CC7.4-procedure",
"type": "procedure",
"description": "Incident analysis procedures"
},
{
"id": "CC7.4-evidence",
"type": "evidence",
"description": "Incident analysis reports"
}
]
},
"CC8.1": {
"code": "CC8.1",
"name": "Change Authorization",
"description": "The organization authorizes, designs, develops or acquires, configures, documents, tests, approves, and implements changes to infrastructure, data, software, and procedures.",
"domain": "Change Management",
"categoryId": "CC8",
"requirements": [
{
"id": "CC8.1-policy",
"type": "policy",
"description": "Change authorization policy",
"policyId": "password_policy"
},
{
"id": "CC8.1-procedure",
"type": "procedure",
"description": "Change authorization procedures"
},
{
"id": "CC8.1-evidence",
"type": "evidence",
"description": "Change authorization records"
}
]
},
"CC9.1": {
"code": "CC9.1",
"name": "Business Continuity Planning",
"description": "The organization identifies, develops, and implements activities to recover critical information technology resources.",
"domain": "Risk Mitigation",
"categoryId": "CC9",
"requirements": [
{
"id": "CC9.1-policy",
"type": "policy",
"description": "Business continuity policy",
"policyId": "password_policy"
},
{
"id": "CC9.1-procedure",
"type": "procedure",
"description": "Business continuity procedures"
},
{
"id": "CC9.1-evidence",
"type": "evidence",
"description": "Business continuity plans"
}
]
},
"CC9.2": {
"code": "CC9.2",
"name": "Vendor Risk Management",
"description": "The organization assesses and manages risks associated with vendors and business partners.",
"domain": "Risk Mitigation",
"categoryId": "CC9",
"requirements": [
{
"id": "CC9.2-policy",
"type": "policy",
"description": "Vendor risk management policy",
"policyId": "password_policy"
},
{
"id": "CC9.2-procedure",
"type": "procedure",
"description": "Vendor risk management procedures"
},
{
"id": "CC9.2-evidence",
"type": "evidence",
"description": "Vendor risk assessments"
}
]
},
"CC9.9": {
"code": "CC9.9",
"name": "Business Continuity and Disaster Recovery Testing",
"description": "The organization tests business continuity and disaster recovery plans, evaluates the test results, and updates the plans accordingly.",
"domain": "Risk Mitigation",
"categoryId": "CC9",
"requirements": [
{
"id": "CC9.9-policy",
"type": "policy",
"description": "BC/DR testing policy",
"policyId": "password_policy"
},
{
"id": "CC9.9-procedure",
"type": "procedure",
"description": "BC/DR testing procedures"
},
{
"id": "CC9.9-evidence",
"type": "evidence",
"description": "BC/DR test results"
}
]
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Review and update policy mappings.

All controls are currently mapped to "password_policy" in their requirements, which appears to be incorrect. Each control should be mapped to its relevant policy. For example:

  • Board oversight control should map to a governance policy
  • Access security control should map to an access control policy
  • Incident response control should map to an incident response policy

Consider updating the policy mappings to match each control's domain and purpose. For instance:

 {
   "CC1.1": {
     "requirements": [
       {
         "type": "policy",
-        "policyId": "password_policy"
+        "policyId": "governance_policy"
       }
     ]
   },
   "CC6.1": {
     "requirements": [
       {
         "type": "policy",
-        "policyId": "password_policy"
+        "policyId": "access_control_policy"
       }
     ]
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{
"CC1.1": {
"code": "CC1.1",
"name": "Board Oversight",
"description": "The board of directors demonstrates independence from management and exercises oversight of the development and performance of internal control.",
"domain": "Control Environment",
"categoryId": "CC1",
"requirements": [
{
"id": "CC1.1-policy",
"type": "policy",
"description": "Board oversight and governance policy",
"policyId": "password_policy"
},
{
"id": "CC1.1-procedure",
"type": "procedure",
"description": "Board oversight procedures"
}
]
},
"CC1.2": {
"code": "CC1.2",
"name": "Management Philosophy",
"description": "Management establishes, with board oversight, structures, reporting lines, and appropriate authorities and responsibilities in the pursuit of objectives.",
"domain": "Control Environment",
"categoryId": "CC1",
"requirements": [
{
"id": "CC1.2-policy",
"type": "policy",
"description": "Organizational structure and management policy",
"policyId": "password_policy"
},
{
"id": "CC1.2-procedure",
"type": "procedure",
"description": "Management oversight procedures"
}
]
},
"CC1.3": {
"code": "CC1.3",
"name": "Organizational Structure",
"description": "The organization demonstrates a commitment to attract, develop, and retain competent individuals in alignment with objectives.",
"domain": "Control Environment",
"categoryId": "CC1",
"requirements": [
{
"id": "CC1.3-policy",
"type": "policy",
"description": "Personnel management policy",
"policyId": "password_policy"
},
{
"id": "CC1.3-procedure",
"type": "procedure",
"description": "Personnel management procedures"
}
]
},
"CC1.4": {
"code": "CC1.4",
"name": "Personnel Policies",
"description": "The organization holds individuals accountable for their internal control responsibilities in the pursuit of objectives.",
"domain": "Control Environment",
"categoryId": "CC1",
"requirements": [
{
"id": "CC1.4-policy",
"type": "policy",
"description": "Personnel accountability policy",
"policyId": "password_policy"
},
{
"id": "CC1.4-procedure",
"type": "procedure",
"description": "Personnel accountability procedures"
},
{
"id": "CC1.4-training",
"type": "training",
"description": "Personnel accountability training"
}
]
},
"CC1.5": {
"code": "CC1.5",
"name": "Code of Conduct",
"description": "The organization demonstrates a commitment to integrity and ethical values.",
"domain": "Control Environment",
"categoryId": "CC1",
"requirements": [
{
"id": "CC1.5-policy",
"type": "policy",
"description": "Code of conduct policy",
"policyId": "password_policy"
},
{
"id": "CC1.5-training",
"type": "training",
"description": "Ethics and integrity training"
}
]
},
"CC2.1": {
"code": "CC2.1",
"name": "Information Quality",
"description": "The organization obtains or generates and uses relevant, quality information to support the functioning of internal control.",
"domain": "Communication",
"categoryId": "CC2",
"requirements": [
{
"id": "CC2.1-policy",
"type": "policy",
"description": "Information quality policy",
"policyId": "password_policy"
},
{
"id": "CC2.1-procedure",
"type": "procedure",
"description": "Information management procedures"
},
{
"id": "CC2.1-evidence",
"type": "evidence",
"description": "Information quality metrics and reports"
}
]
},
"CC2.2": {
"code": "CC2.2",
"name": "Internal Communication",
"description": "The organization internally communicates information, including objectives and responsibilities for internal control.",
"domain": "Communication",
"categoryId": "CC2",
"requirements": [
{
"id": "CC2.2-policy",
"type": "policy",
"description": "Internal communication policy",
"policyId": "password_policy"
},
{
"id": "CC2.2-procedure",
"type": "procedure",
"description": "Communication procedures"
},
{
"id": "CC2.2-evidence",
"type": "evidence",
"description": "Internal communication records"
}
]
},
"CC2.3": {
"code": "CC2.3",
"name": "External Communication",
"description": "The organization communicates with external parties regarding matters affecting the functioning of internal control.",
"domain": "Communication",
"categoryId": "CC2",
"requirements": [
{
"id": "CC2.3-policy",
"type": "policy",
"description": "External communication policy",
"policyId": "password_policy"
},
{
"id": "CC2.3-procedure",
"type": "procedure",
"description": "External communication procedures"
},
{
"id": "CC2.3-evidence",
"type": "evidence",
"description": "External communication records"
}
]
},
"CC3.1": {
"code": "CC3.1",
"name": "Risk Assessment Process",
"description": "The organization specifies objectives with sufficient clarity to enable the identification and assessment of risks relating to objectives.",
"domain": "Risk Assessment",
"categoryId": "CC3",
"requirements": [
{
"id": "CC3.1-policy",
"type": "policy",
"description": "Risk assessment policy",
"policyId": "password_policy"
},
{
"id": "CC3.1-procedure",
"type": "procedure",
"description": "Risk assessment procedures"
},
{
"id": "CC3.1-evidence",
"type": "evidence",
"description": "Risk assessment documentation"
}
]
},
"CC3.2": {
"code": "CC3.2",
"name": "Risk Identification",
"description": "The organization 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.",
"domain": "Risk Assessment",
"categoryId": "CC3",
"requirements": [
{
"id": "CC3.2-policy",
"type": "policy",
"description": "Risk identification policy",
"policyId": "password_policy"
},
{
"id": "CC3.2-procedure",
"type": "procedure",
"description": "Risk identification procedures"
},
{
"id": "CC3.2-evidence",
"type": "evidence",
"description": "Risk register and analysis"
}
]
},
"CC3.3": {
"code": "CC3.3",
"name": "Fraud Risk Assessment",
"description": "The organization considers the potential for fraud in assessing risks to the achievement of objectives.",
"domain": "Risk Assessment",
"categoryId": "CC3",
"requirements": [
{
"id": "CC3.3-policy",
"type": "policy",
"description": "Fraud risk assessment policy",
"policyId": "password_policy"
},
{
"id": "CC3.3-procedure",
"type": "procedure",
"description": "Fraud risk assessment procedures"
},
{
"id": "CC3.3-evidence",
"type": "evidence",
"description": "Fraud risk assessment reports"
}
]
},
"CC3.4": {
"code": "CC3.4",
"name": "Change Management Risk",
"description": "The organization identifies and assesses changes that could significantly impact the system of internal control.",
"domain": "Risk Assessment",
"categoryId": "CC3",
"requirements": [
{
"id": "CC3.4-policy",
"type": "policy",
"description": "Change management risk policy",
"policyId": "password_policy"
},
{
"id": "CC3.4-procedure",
"type": "procedure",
"description": "Change impact assessment procedures"
},
{
"id": "CC3.4-evidence",
"type": "evidence",
"description": "Change impact assessments"
}
]
},
"CC4.1": {
"code": "CC4.1",
"name": "Control Monitoring",
"description": "The organization selects, develops, and performs ongoing and/or separate evaluations to ascertain whether the components of internal control are present and functioning.",
"domain": "Monitoring Activities",
"categoryId": "CC4",
"requirements": [
{
"id": "CC4.1-policy",
"type": "policy",
"description": "Control monitoring policy",
"policyId": "password_policy"
},
{
"id": "CC4.1-procedure",
"type": "procedure",
"description": "Control monitoring procedures"
},
{
"id": "CC4.1-evidence",
"type": "evidence",
"description": "Control monitoring reports"
}
]
},
"CC4.2": {
"code": "CC4.2",
"name": "Deficiency Management",
"description": "The organization evaluates and communicates internal control deficiencies in a timely manner to those parties responsible for taking corrective action.",
"domain": "Monitoring Activities",
"categoryId": "CC4",
"requirements": [
{
"id": "CC4.2-policy",
"type": "policy",
"description": "Deficiency management policy",
"policyId": "password_policy"
},
{
"id": "CC4.2-procedure",
"type": "procedure",
"description": "Deficiency management procedures"
},
{
"id": "CC4.2-evidence",
"type": "evidence",
"description": "Deficiency tracking and resolution reports"
}
]
},
"CC5.1": {
"code": "CC5.1",
"name": "Control Selection",
"description": "The organization selects and develops control activities that contribute to the mitigation of risks to the achievement of objectives to acceptable levels.",
"domain": "Control Activities",
"categoryId": "CC5",
"requirements": [
{
"id": "CC5.1-policy",
"type": "policy",
"description": "Control selection policy",
"policyId": "password_policy"
},
{
"id": "CC5.1-procedure",
"type": "procedure",
"description": "Control selection procedures"
},
{
"id": "CC5.1-evidence",
"type": "evidence",
"description": "Control selection documentation"
}
]
},
"CC5.2": {
"code": "CC5.2",
"name": "Technology Controls",
"description": "The organization selects and develops general control activities over technology to support the achievement of objectives.",
"domain": "Control Activities",
"categoryId": "CC5",
"requirements": [
{
"id": "CC5.2-policy",
"type": "policy",
"description": "Technology control policy",
"policyId": "password_policy"
},
{
"id": "CC5.2-procedure",
"type": "procedure",
"description": "Technology control procedures"
},
{
"id": "CC5.2-evidence",
"type": "evidence",
"description": "Technology control documentation"
}
]
},
"CC5.3": {
"code": "CC5.3",
"name": "Policy Implementation",
"description": "The organization deploys control activities through policies that establish what is expected and procedures that put policies into action.",
"domain": "Control Activities",
"categoryId": "CC5",
"requirements": [
{
"id": "CC5.3-policy",
"type": "policy",
"description": "Policy implementation framework",
"policyId": "password_policy"
},
{
"id": "CC5.3-procedure",
"type": "procedure",
"description": "Policy implementation procedures"
},
{
"id": "CC5.3-evidence",
"type": "evidence",
"description": "Policy implementation records"
}
]
},
"CC6.1": {
"code": "CC6.1",
"name": "Access Security",
"description": "The organization implements logical access security software, infrastructure, and architectures over protected information assets to protect them from security events.",
"domain": "Logical and Physical Access Controls",
"categoryId": "CC6",
"requirements": [
{
"id": "CC6.1-policy",
"type": "policy",
"description": "Access security policy",
"policyId": "password_policy"
},
{
"id": "CC6.1-procedure",
"type": "procedure",
"description": "Access security procedures"
},
{
"id": "CC6.1-evidence",
"type": "evidence",
"description": "Access security configurations"
}
]
},
"CC6.2": {
"code": "CC6.2",
"name": "Access Authentication",
"description": "Prior to issuing system credentials and granting system access, the organization registers and authorizes new internal and external users.",
"domain": "Logical and Physical Access Controls",
"categoryId": "CC6",
"requirements": [
{
"id": "CC6.2-policy",
"type": "policy",
"description": "User authentication policy",
"policyId": "password_policy"
},
{
"id": "CC6.2-procedure",
"type": "procedure",
"description": "User authentication procedures"
},
{
"id": "CC6.2-evidence",
"type": "evidence",
"description": "User authentication records"
}
]
},
"CC6.3": {
"code": "CC6.3",
"name": "Access Removal",
"description": "The organization removes access to protected information assets when appropriate.",
"domain": "Logical and Physical Access Controls",
"categoryId": "CC6",
"requirements": [
{
"id": "CC6.3-policy",
"type": "policy",
"description": "Access removal policy",
"policyId": "password_policy"
},
{
"id": "CC6.3-procedure",
"type": "procedure",
"description": "Access removal procedures"
},
{
"id": "CC6.3-evidence",
"type": "evidence",
"description": "Access removal records"
}
]
},
"CC6.4": {
"code": "CC6.4",
"name": "Access Review",
"description": "The organization evaluates and manages access to protected information assets on a periodic basis.",
"domain": "Logical and Physical Access Controls",
"categoryId": "CC6",
"requirements": [
{
"id": "CC6.4-policy",
"type": "policy",
"description": "Access review policy",
"policyId": "password_policy"
},
{
"id": "CC6.4-procedure",
"type": "procedure",
"description": "Access review procedures"
},
{
"id": "CC6.4-evidence",
"type": "evidence",
"description": "Access review records"
}
]
},
"CC6.5": {
"code": "CC6.5",
"name": "System Account Management",
"description": "The organization identifies and authenticates system users, devices, and other systems before allowing access.",
"domain": "Logical and Physical Access Controls",
"categoryId": "CC6",
"requirements": [
{
"id": "CC6.5-policy",
"type": "policy",
"description": "System account management policy",
"policyId": "password_policy"
},
{
"id": "CC6.5-procedure",
"type": "procedure",
"description": "System account management procedures"
},
{
"id": "CC6.5-evidence",
"type": "evidence",
"description": "System account management records"
}
]
},
"CC6.6": {
"code": "CC6.6",
"name": "Access Restrictions",
"description": "The organization restricts physical access to facilities and protected information assets.",
"domain": "Logical and Physical Access Controls",
"categoryId": "CC6",
"requirements": [
{
"id": "CC6.6-policy",
"type": "policy",
"description": "Physical access policy",
"policyId": "password_policy"
},
{
"id": "CC6.6-procedure",
"type": "procedure",
"description": "Physical access procedures"
},
{
"id": "CC6.6-evidence",
"type": "evidence",
"description": "Physical access records"
}
]
},
"CC6.7": {
"code": "CC6.7",
"name": "Information Asset Changes",
"description": "The organization manages changes to system components to minimize the risk of unauthorized changes.",
"domain": "Logical and Physical Access Controls",
"categoryId": "CC6",
"requirements": [
{
"id": "CC6.7-policy",
"type": "policy",
"description": "Change management policy",
"policyId": "password_policy"
},
{
"id": "CC6.7-procedure",
"type": "procedure",
"description": "Change management procedures"
},
{
"id": "CC6.7-evidence",
"type": "evidence",
"description": "Change management records"
}
]
},
"CC6.8": {
"code": "CC6.8",
"name": "Malicious Software Prevention",
"description": "The organization implements controls to prevent or detect and act upon the introduction of unauthorized or malicious software.",
"domain": "Logical and Physical Access Controls",
"categoryId": "CC6",
"requirements": [
{
"id": "CC6.8-policy",
"type": "policy",
"description": "Malware prevention policy",
"policyId": "password_policy"
},
{
"id": "CC6.8-procedure",
"type": "procedure",
"description": "Malware prevention procedures"
},
{
"id": "CC6.8-evidence",
"type": "evidence",
"description": "Malware prevention configurations"
}
]
},
"CC7.1": {
"code": "CC7.1",
"name": "Infrastructure Monitoring",
"description": "To detect and act upon security events in a timely manner, the organization monitors system capacity, security threats, changing regulatory requirements, and other system vulnerabilities.",
"domain": "System Operations",
"categoryId": "CC7",
"requirements": [
{
"id": "CC7.1-policy",
"type": "policy",
"description": "Infrastructure monitoring policy",
"policyId": "password_policy"
},
{
"id": "CC7.1-procedure",
"type": "procedure",
"description": "Infrastructure monitoring procedures"
},
{
"id": "CC7.1-evidence",
"type": "evidence",
"description": "Infrastructure monitoring reports"
}
]
},
"CC7.2": {
"code": "CC7.2",
"name": "Security Event Response",
"description": "The organization designs, develops, and implements policies and procedures to respond to security incidents and breaches.",
"domain": "System Operations",
"categoryId": "CC7",
"requirements": [
{
"id": "CC7.2-policy",
"type": "policy",
"description": "Incident response policy",
"policyId": "password_policy"
},
{
"id": "CC7.2-procedure",
"type": "procedure",
"description": "Incident response procedures"
},
{
"id": "CC7.2-evidence",
"type": "evidence",
"description": "Incident response records"
}
]
},
"CC7.3": {
"code": "CC7.3",
"name": "Security Event Recovery",
"description": "The organization implements recovery procedures to ensure timely restoration of systems or assets affected by security incidents.",
"domain": "System Operations",
"categoryId": "CC7",
"requirements": [
{
"id": "CC7.3-policy",
"type": "policy",
"description": "Recovery policy",
"policyId": "password_policy"
},
{
"id": "CC7.3-procedure",
"type": "procedure",
"description": "Recovery procedures"
},
{
"id": "CC7.3-evidence",
"type": "evidence",
"description": "Recovery test results"
}
]
},
"CC7.4": {
"code": "CC7.4",
"name": "Security Event Analysis",
"description": "The organization implements incident response activities to identify root causes of security incidents and develop remediation plans.",
"domain": "System Operations",
"categoryId": "CC7",
"requirements": [
{
"id": "CC7.4-policy",
"type": "policy",
"description": "Incident analysis policy",
"policyId": "password_policy"
},
{
"id": "CC7.4-procedure",
"type": "procedure",
"description": "Incident analysis procedures"
},
{
"id": "CC7.4-evidence",
"type": "evidence",
"description": "Incident analysis reports"
}
]
},
"CC8.1": {
"code": "CC8.1",
"name": "Change Authorization",
"description": "The organization authorizes, designs, develops or acquires, configures, documents, tests, approves, and implements changes to infrastructure, data, software, and procedures.",
"domain": "Change Management",
"categoryId": "CC8",
"requirements": [
{
"id": "CC8.1-policy",
"type": "policy",
"description": "Change authorization policy",
"policyId": "password_policy"
},
{
"id": "CC8.1-procedure",
"type": "procedure",
"description": "Change authorization procedures"
},
{
"id": "CC8.1-evidence",
"type": "evidence",
"description": "Change authorization records"
}
]
},
"CC9.1": {
"code": "CC9.1",
"name": "Business Continuity Planning",
"description": "The organization identifies, develops, and implements activities to recover critical information technology resources.",
"domain": "Risk Mitigation",
"categoryId": "CC9",
"requirements": [
{
"id": "CC9.1-policy",
"type": "policy",
"description": "Business continuity policy",
"policyId": "password_policy"
},
{
"id": "CC9.1-procedure",
"type": "procedure",
"description": "Business continuity procedures"
},
{
"id": "CC9.1-evidence",
"type": "evidence",
"description": "Business continuity plans"
}
]
},
"CC9.2": {
"code": "CC9.2",
"name": "Vendor Risk Management",
"description": "The organization assesses and manages risks associated with vendors and business partners.",
"domain": "Risk Mitigation",
"categoryId": "CC9",
"requirements": [
{
"id": "CC9.2-policy",
"type": "policy",
"description": "Vendor risk management policy",
"policyId": "password_policy"
},
{
"id": "CC9.2-procedure",
"type": "procedure",
"description": "Vendor risk management procedures"
},
{
"id": "CC9.2-evidence",
"type": "evidence",
"description": "Vendor risk assessments"
}
]
},
"CC9.9": {
"code": "CC9.9",
"name": "Business Continuity and Disaster Recovery Testing",
"description": "The organization tests business continuity and disaster recovery plans, evaluates the test results, and updates the plans accordingly.",
"domain": "Risk Mitigation",
"categoryId": "CC9",
"requirements": [
{
"id": "CC9.9-policy",
"type": "policy",
"description": "BC/DR testing policy",
"policyId": "password_policy"
},
{
"id": "CC9.9-procedure",
"type": "procedure",
"description": "BC/DR testing procedures"
},
{
"id": "CC9.9-evidence",
"type": "evidence",
"description": "BC/DR test results"
}
]
}
}
{
"CC1.1": {
"code": "CC1.1",
"name": "Board Oversight",
"description": "The board of directors demonstrates independence from management and exercises oversight of the development and performance of internal control.",
"domain": "Control Environment",
"categoryId": "CC1",
"requirements": [
{
"id": "CC1.1-policy",
"type": "policy",
"description": "Board oversight and governance policy",
"policyId": "governance_policy"
},
{
"id": "CC1.1-procedure",
"type": "procedure",
"description": "Board oversight procedures"
}
]
},
"CC1.2": {
"code": "CC1.2",
"name": "Management Philosophy",
"description": "Management establishes, with board oversight, structures, reporting lines, and appropriate authorities and responsibilities in the pursuit of objectives.",
"domain": "Control Environment",
"categoryId": "CC1",
"requirements": [
{
"id": "CC1.2-policy",
"type": "policy",
"description": "Organizational structure and management policy",
"policyId": "password_policy"
},
{
"id": "CC1.2-procedure",
"type": "procedure",
"description": "Management oversight procedures"
}
]
},
"CC1.3": {
"code": "CC1.3",
"name": "Organizational Structure",
"description": "The organization demonstrates a commitment to attract, develop, and retain competent individuals in alignment with objectives.",
"domain": "Control Environment",
"categoryId": "CC1",
"requirements": [
{
"id": "CC1.3-policy",
"type": "policy",
"description": "Personnel management policy",
"policyId": "password_policy"
},
{
"id": "CC1.3-procedure",
"type": "procedure",
"description": "Personnel management procedures"
}
]
},
"CC1.4": {
"code": "CC1.4",
"name": "Personnel Policies",
"description": "The organization holds individuals accountable for their internal control responsibilities in the pursuit of objectives.",
"domain": "Control Environment",
"categoryId": "CC1",
"requirements": [
{
"id": "CC1.4-policy",
"type": "policy",
"description": "Personnel accountability policy",
"policyId": "password_policy"
},
{
"id": "CC1.4-procedure",
"type": "procedure",
"description": "Personnel accountability procedures"
},
{
"id": "CC1.4-training",
"type": "training",
"description": "Personnel accountability training"
}
]
},
"CC1.5": {
"code": "CC1.5",
"name": "Code of Conduct",
"description": "The organization demonstrates a commitment to integrity and ethical values.",
"domain": "Control Environment",
"categoryId": "CC1",
"requirements": [
{
"id": "CC1.5-policy",
"type": "policy",
"description": "Code of conduct policy",
"policyId": "password_policy"
},
{
"id": "CC1.5-training",
"type": "training",
"description": "Ethics and integrity training"
}
]
},
"CC2.1": {
"code": "CC2.1",
"name": "Information Quality",
"description": "The organization obtains or generates and uses relevant, quality information to support the functioning of internal control.",
"domain": "Communication",
"categoryId": "CC2",
"requirements": [
{
"id": "CC2.1-policy",
"type": "policy",
"description": "Information quality policy",
"policyId": "password_policy"
},
{
"id": "CC2.1-procedure",
"type": "procedure",
"description": "Information management procedures"
},
{
"id": "CC2.1-evidence",
"type": "evidence",
"description": "Information quality metrics and reports"
}
]
},
"CC2.2": {
"code": "CC2.2",
"name": "Internal Communication",
"description": "The organization internally communicates information, including objectives and responsibilities for internal control.",
"domain": "Communication",
"categoryId": "CC2",
"requirements": [
{
"id": "CC2.2-policy",
"type": "policy",
"description": "Internal communication policy",
"policyId": "password_policy"
},
{
"id": "CC2.2-procedure",
"type": "procedure",
"description": "Communication procedures"
},
{
"id": "CC2.2-evidence",
"type": "evidence",
"description": "Internal communication records"
}
]
},
"CC2.3": {
"code": "CC2.3",
"name": "External Communication",
"description": "The organization communicates with external parties regarding matters affecting the functioning of internal control.",
"domain": "Communication",
"categoryId": "CC2",
"requirements": [
{
"id": "CC2.3-policy",
"type": "policy",
"description": "External communication policy",
"policyId": "password_policy"
},
{
"id": "CC2.3-procedure",
"type": "procedure",
"description": "External communication procedures"
},
{
"id": "CC2.3-evidence",
"type": "evidence",
"description": "External communication records"
}
]
},
"CC3.1": {
"code": "CC3.1",
"name": "Risk Assessment Process",
"description": "The organization specifies objectives with sufficient clarity to enable the identification and assessment of risks relating to objectives.",
"domain": "Risk Assessment",
"categoryId": "CC3",
"requirements": [
{
"id": "CC3.1-policy",
"type": "policy",
"description": "Risk assessment policy",
"policyId": "password_policy"
},
{
"id": "CC3.1-procedure",
"type": "procedure",
"description": "Risk assessment procedures"
},
{
"id": "CC3.1-evidence",
"type": "evidence",
"description": "Risk assessment documentation"
}
]
},
"CC3.2": {
"code": "CC3.2",
"name": "Risk Identification",
"description": "The organization 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.",
"domain": "Risk Assessment",
"categoryId": "CC3",
"requirements": [
{
"id": "CC3.2-policy",
"type": "policy",
"description": "Risk identification policy",
"policyId": "password_policy"
},
{
"id": "CC3.2-procedure",
"type": "procedure",
"description": "Risk identification procedures"
},
{
"id": "CC3.2-evidence",
"type": "evidence",
"description": "Risk register and analysis"
}
]
},
"CC3.3": {
"code": "CC3.3",
"name": "Fraud Risk Assessment",
"description": "The organization considers the potential for fraud in assessing risks to the achievement of objectives.",
"domain": "Risk Assessment",
"categoryId": "CC3",
"requirements": [
{
"id": "CC3.3-policy",
"type": "policy",
"description": "Fraud risk assessment policy",
"policyId": "password_policy"
},
{
"id": "CC3.3-procedure",
"type": "procedure",
"description": "Fraud risk assessment procedures"
},
{
"id": "CC3.3-evidence",
"type": "evidence",
"description": "Fraud risk assessment reports"
}
]
},
"CC3.4": {
"code": "CC3.4",
"name": "Change Management Risk",
"description": "The organization identifies and assesses changes that could significantly impact the system of internal control.",
"domain": "Risk Assessment",
"categoryId": "CC3",
"requirements": [
{
"id": "CC3.4-policy",
"type": "policy",
"description": "Change management risk policy",
"policyId": "password_policy"
},
{
"id": "CC3.4-procedure",
"type": "procedure",
"description": "Change impact assessment procedures"
},
{
"id": "CC3.4-evidence",
"type": "evidence",
"description": "Change impact assessments"
}
]
},
"CC4.1": {
"code": "CC4.1",
"name": "Control Monitoring",
"description": "The organization selects, develops, and performs ongoing and/or separate evaluations to ascertain whether the components of internal control are present and functioning.",
"domain": "Monitoring Activities",
"categoryId": "CC4",
"requirements": [
{
"id": "CC4.1-policy",
"type": "policy",
"description": "Control monitoring policy",
"policyId": "password_policy"
},
{
"id": "CC4.1-procedure",
"type": "procedure",
"description": "Control monitoring procedures"
},
{
"id": "CC4.1-evidence",
"type": "evidence",
"description": "Control monitoring reports"
}
]
},
"CC4.2": {
"code": "CC4.2",
"name": "Deficiency Management",
"description": "The organization evaluates and communicates internal control deficiencies in a timely manner to those parties responsible for taking corrective action.",
"domain": "Monitoring Activities",
"categoryId": "CC4",
"requirements": [
{
"id": "CC4.2-policy",
"type": "policy",
"description": "Deficiency management policy",
"policyId": "password_policy"
},
{
"id": "CC4.2-procedure",
"type": "procedure",
"description": "Deficiency management procedures"
},
{
"id": "CC4.2-evidence",
"type": "evidence",
"description": "Deficiency tracking and resolution reports"
}
]
},
"CC5.1": {
"code": "CC5.1",
"name": "Control Selection",
"description": "The organization selects and develops control activities that contribute to the mitigation of risks to the achievement of objectives to acceptable levels.",
"domain": "Control Activities",
"categoryId": "CC5",
"requirements": [
{
"id": "CC5.1-policy",
"type": "policy",
"description": "Control selection policy",
"policyId": "password_policy"
},
{
"id": "CC5.1-procedure",
"type": "procedure",
"description": "Control selection procedures"
},
{
"id": "CC5.1-evidence",
"type": "evidence",
"description": "Control selection documentation"
}
]
},
"CC5.2": {
"code": "CC5.2",
"name": "Technology Controls",
"description": "The organization selects and develops general control activities over technology to support the achievement of objectives.",
"domain": "Control Activities",
"categoryId": "CC5",
"requirements": [
{
"id": "CC5.2-policy",
"type": "policy",
"description": "Technology control policy",
"policyId": "password_policy"
},
{
"id": "CC5.2-procedure",
"type": "procedure",
"description": "Technology control procedures"
},
{
"id": "CC5.2-evidence",
"type": "evidence",
"description": "Technology control documentation"
}
]
},
"CC5.3": {
"code": "CC5.3",
"name": "Policy Implementation",
"description": "The organization deploys control activities through policies that establish what is expected and procedures that put policies into action.",
"domain": "Control Activities",
"categoryId": "CC5",
"requirements": [
{
"id": "CC5.3-policy",
"type": "policy",
"description": "Policy implementation framework",
"policyId": "password_policy"
},
{
"id": "CC5.3-procedure",
"type": "procedure",
"description": "Policy implementation procedures"
},
{
"id": "CC5.3-evidence",
"type": "evidence",
"description": "Policy implementation records"
}
]
},
"CC6.1": {
"code": "CC6.1",
"name": "Access Security",
"description": "The organization implements logical access security software, infrastructure, and architectures over protected information assets to protect them from security events.",
"domain": "Logical and Physical Access Controls",
"categoryId": "CC6",
"requirements": [
{
"id": "CC6.1-policy",
"type": "policy",
"description": "Access security policy",
"policyId": "access_control_policy"
},
{
"id": "CC6.1-procedure",
"type": "procedure",
"description": "Access security procedures"
},
{
"id": "CC6.1-evidence",
"type": "evidence",
"description": "Access security configurations"
}
]
},
"CC6.2": {
"code": "CC6.2",
"name": "Access Authentication",
"description": "Prior to issuing system credentials and granting system access, the organization registers and authorizes new internal and external users.",
"domain": "Logical and Physical Access Controls",
"categoryId": "CC6",
"requirements": [
{
"id": "CC6.2-policy",
"type": "policy",
"description": "User authentication policy",
"policyId": "password_policy"
},
{
"id": "CC6.2-procedure",
"type": "procedure",
"description": "User authentication procedures"
},
{
"id": "CC6.2-evidence",
"type": "evidence",
"description": "User authentication records"
}
]
},
"CC6.3": {
"code": "CC6.3",
"name": "Access Removal",
"description": "The organization removes access to protected information assets when appropriate.",
"domain": "Logical and Physical Access Controls",
"categoryId": "CC6",
"requirements": [
{
"id": "CC6.3-policy",
"type": "policy",
"description": "Access removal policy",
"policyId": "password_policy"
},
{
"id": "CC6.3-procedure",
"type": "procedure",
"description": "Access removal procedures"
},
{
"id": "CC6.3-evidence",
"type": "evidence",
"description": "Access removal records"
}
]
},
"CC6.4": {
"code": "CC6.4",
"name": "Access Review",
"description": "The organization evaluates and manages access to protected information assets on a periodic basis.",
"domain": "Logical and Physical Access Controls",
"categoryId": "CC6",
"requirements": [
{
"id": "CC6.4-policy",
"type": "policy",
"description": "Access review policy",
"policyId": "password_policy"
},
{
"id": "CC6.4-procedure",
"type": "procedure",
"description": "Access review procedures"
},
{
"id": "CC6.4-evidence",
"type": "evidence",
"description": "Access review records"
}
]
},
"CC6.5": {
"code": "CC6.5",
"name": "System Account Management",
"description": "The organization identifies and authenticates system users, devices, and other systems before allowing access.",
"domain": "Logical and Physical Access Controls",
"categoryId": "CC6",
"requirements": [
{
"id": "CC6.5-policy",
"type": "policy",
"description": "System account management policy",
"policyId": "password_policy"
},
{
"id": "CC6.5-procedure",
"type": "procedure",
"description": "System account management procedures"
},
{
"id": "CC6.5-evidence",
"type": "evidence",
"description": "System account management records"
}
]
},
"CC6.6": {
"code": "CC6.6",
"name": "Access Restrictions",
"description": "The organization restricts physical access to facilities and protected information assets.",
"domain": "Logical and Physical Access Controls",
"categoryId": "CC6",
"requirements": [
{
"id": "CC6.6-policy",
"type": "policy",
"description": "Physical access policy",
"policyId": "password_policy"
},
{
"id": "CC6.6-procedure",
"type": "procedure",
"description": "Physical access procedures"
},
{
"id": "CC6.6-evidence",
"type": "evidence",
"description": "Physical access records"
}
]
},
"CC6.7": {
"code": "CC6.7",
"name": "Information Asset Changes",
"description": "The organization manages changes to system components to minimize the risk of unauthorized changes.",
"domain": "Logical and Physical Access Controls",
"categoryId": "CC6",
"requirements": [
{
"id": "CC6.7-policy",
"type": "policy",
"description": "Change management policy",
"policyId": "password_policy"
},
{
"id": "CC6.7-procedure",
"type": "procedure",
"description": "Change management procedures"
},
{
"id": "CC6.7-evidence",
"type": "evidence",
"description": "Change management records"
}
]
},
"CC6.8": {
"code": "CC6.8",
"name": "Malicious Software Prevention",
"description": "The organization implements controls to prevent or detect and act upon the introduction of unauthorized or malicious software.",
"domain": "Logical and Physical Access Controls",
"categoryId": "CC6",
"requirements": [
{
"id": "CC6.8-policy",
"type": "policy",
"description": "Malware prevention policy",
"policyId": "password_policy"
},
{
"id": "CC6.8-procedure",
"type": "procedure",
"description": "Malware prevention procedures"
},
{
"id": "CC6.8-evidence",
"type": "evidence",
"description": "Malware prevention configurations"
}
]
},
"CC7.1": {
"code": "CC7.1",
"name": "Infrastructure Monitoring",
"description": "To detect and act upon security events in a timely manner, the organization monitors system capacity, security threats, changing regulatory requirements, and other system vulnerabilities.",
"domain": "System Operations",
"categoryId": "CC7",
"requirements": [
{
"id": "CC7.1-policy",
"type": "policy",
"description": "Infrastructure monitoring policy",
"policyId": "password_policy"
},
{
"id": "CC7.1-procedure",
"type": "procedure",
"description": "Infrastructure monitoring procedures"
},
{
"id": "CC7.1-evidence",
"type": "evidence",
"description": "Infrastructure monitoring reports"
}
]
},
"CC7.2": {
"code": "CC7.2",
"name": "Security Event Response",
"description": "The organization designs, develops, and implements policies and procedures to respond to security incidents and breaches.",
"domain": "System Operations",
"categoryId": "CC7",
"requirements": [
{
"id": "CC7.2-policy",
"type": "policy",
"description": "Incident response policy",
"policyId": "password_policy"
},
{
"id": "CC7.2-procedure",
"type": "procedure",
"description": "Incident response procedures"
},
{
"id": "CC7.2-evidence",
"type": "evidence",
"description": "Incident response records"
}
]
},
"CC7.3": {
"code": "CC7.3",
"name": "Security Event Recovery",
"description": "The organization implements recovery procedures to ensure timely restoration of systems or assets affected by security incidents.",
"domain": "System Operations",
"categoryId": "CC7",
"requirements": [
{
"id": "CC7.3-policy",
"type": "policy",
"description": "Recovery policy",
"policyId": "password_policy"
},
{
"id": "CC7.3-procedure",
"type": "procedure",
"description": "Recovery procedures"
},
{
"id": "CC7.3-evidence",
"type": "evidence",
"description": "Recovery test results"
}
]
},
"CC7.4": {
"code": "CC7.4",
"name": "Security Event Analysis",
"description": "The organization implements incident response activities to identify root causes of security incidents and develop remediation plans.",
"domain": "System Operations",
"categoryId": "CC7",
"requirements": [
{
"id": "CC7.4-policy",
"type": "policy",
"description": "Incident analysis policy",
"policyId": "password_policy"
},
{
"id": "CC7.4-procedure",
"type": "procedure",
"description": "Incident analysis procedures"
},
{
"id": "CC7.4-evidence",
"type": "evidence",
"description": "Incident analysis reports"
}
]
},
"CC8.1": {
"code": "CC8.1",
"name": "Change Authorization",
"description": "The organization authorizes, designs, develops or acquires, configures, documents, tests, approves, and implements changes to infrastructure, data, software, and procedures.",
"domain": "Change Management",
"categoryId": "CC8",
"requirements": [
{
"id": "CC8.1-policy",
"type": "policy",
"description": "Change authorization policy",
"policyId": "password_policy"
},
{
"id": "CC8.1-procedure",
"type": "procedure",
"description": "Change authorization procedures"
},
{
"id": "CC8.1-evidence",
"type": "evidence",
"description": "Change authorization records"
}
]
},
"CC9.1": {
"code": "CC9.1",
"name": "Business Continuity Planning",
"description": "The organization identifies, develops, and implements activities to recover critical information technology resources.",
"domain": "Risk Mitigation",
"categoryId": "CC9",
"requirements": [
{
"id": "CC9.1-policy",
"type": "policy",
"description": "Business continuity policy",
"policyId": "password_policy"
},
{
"id": "CC9.1-procedure",
"type": "procedure",
"description": "Business continuity procedures"
},
{
"id": "CC9.1-evidence",
"type": "evidence",
"description": "Business continuity plans"
}
]
},
"CC9.2": {
"code": "CC9.2",
"name": "Vendor Risk Management",
"description": "The organization assesses and manages risks associated with vendors and business partners.",
"domain": "Risk Mitigation",
"categoryId": "CC9",
"requirements": [
{
"id": "CC9.2-policy",
"type": "policy",
"description": "Vendor risk management policy",
"policyId": "password_policy"
},
{
"id": "CC9.2-procedure",
"type": "procedure",
"description": "Vendor risk management procedures"
},
{
"id": "CC9.2-evidence",
"type": "evidence",
"description": "Vendor risk assessments"
}
]
},
"CC9.9": {
"code": "CC9.9",
"name": "Business Continuity and Disaster Recovery Testing",
"description": "The organization tests business continuity and disaster recovery plans, evaluates the test results, and updates the plans accordingly.",
"domain": "Risk Mitigation",
"categoryId": "CC9",
"requirements": [
{
"id": "CC9.9-policy",
"type": "policy",
"description": "BC/DR testing policy",
"policyId": "password_policy"
},
{
"id": "CC9.9-procedure",
"type": "procedure",
"description": "BC/DR testing procedures"
},
{
"id": "CC9.9-evidence",
"type": "evidence",
"description": "BC/DR test results"
}
]
}
}

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🔭 Outside diff range comments (1)
apps/app/src/actions/framework/get-framework-categories.ts (1)

79-79: 🛠️ Refactor suggestion

Enhance error logging security.

The current error logging might expose sensitive information. Consider sanitizing the error object or using a structured logging approach.

-      console.error("Error fetching framework categories:", error);
+      console.error("Error fetching framework categories:", {
+        message: error instanceof Error ? error.message : "Unknown error",
+        frameworkId,
+        organizationId: user.organizationId
+      });
🧹 Nitpick comments (4)
apps/app/src/actions/framework/get-framework-categories.ts (1)

81-83: Consider providing more specific error messages.

The generic error message doesn't help users understand what went wrong. Consider categorizing errors and providing more context.

-        error: "Failed to fetch framework categories",
+        error: error instanceof Error 
+          ? `Failed to fetch framework categories: ${error.message}`
+          : "Failed to fetch framework categories due to an unknown error",
apps/app/src/app/[locale]/(app)/(dashboard)/frameworks/[frameworkId]/page.tsx (3)

50-69: Optimize cache key strategy.

The current cache key is static. Consider including dynamic parameters in the cache key for better granularity.

 const getFramework = unstable_cache(
   async (frameworkId: string) => {
     return db.framework.findFirst({
       where: {
         id: frameworkId,
       },
       include: {
         categories: {
           include: {
             controls: true,
           },
         },
       },
     });
   },
-  ["framework-cache"],
+  [`framework-${frameworkId}`],
   {
-    tags: ["framework-cache"],
+    tags: ["framework-cache", `framework-${frameworkId}`],
   }
 );

71-92: Enhance cache key for organization-specific data.

Similar to the framework cache, the organization framework cache could benefit from more specific cache keys.

 const getOrganizationFramework = unstable_cache(
   async (frameworkId: string, organizationId: string) => {
     return db.organizationFramework.findFirst({
       where: {
         frameworkId,
         organizationId,
       },
       include: {
         organizationControl: {
           include: {
             control: true,
             artifacts: true,
           },
         },
       },
     });
   },
-  ["org-framework-cache"],
+  [`org-framework-${organizationId}-${frameworkId}`],
   {
-    tags: ["org-framework-cache"],
+    tags: ["org-framework-cache", `org-framework-${organizationId}`],
   }
 );

94-134: Apply consistent cache key strategy.

For consistency with other caching functions, enhance the framework categories cache key.

 const getFrameworkCategories = unstable_cache(
   async (frameworkId: string, organizationId: string) => {
     // ... existing implementation ...
   },
-  ["framework-categories-cache"],
+  [`framework-categories-${organizationId}-${frameworkId}`],
   {
-    tags: ["framework-categories-cache"],
+    tags: ["framework-categories-cache", `framework-categories-${organizationId}`],
   }
 );
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f7df90e and 391cf48.

⛔ Files ignored due to path filters (2)
  • bun.lockb is excluded by !**/bun.lockb
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (5)
  • apps/app/package.json (2 hunks)
  • apps/app/src/actions/framework/get-framework-categories.ts (2 hunks)
  • apps/app/src/app/[locale]/(app)/(dashboard)/frameworks/[frameworkId]/page.tsx (4 hunks)
  • apps/app/src/components/frameworks/framework-controls.tsx (1 hunks)
  • apps/app/src/jobs/tasks/notifications/risk-task-notification.ts (3 hunks)
🔇 Additional comments (4)
apps/app/src/jobs/tasks/notifications/risk-task-notification.ts (3)

2-6: LGTM! Import statements are now more readable.

The multi-line format improves readability while maintaining the same functionality.


24-24: LGTM! Consistent formatting.

The formatting change maintains code style consistency.


32-34: LGTM! Improved readability of the conditional assignment.

The multi-line format makes the ternary operation easier to read while maintaining the same logic.

apps/app/package.json (1)

17-17: LGTM! Correct dependency categorization.

Moving @bubba/notifications to dependencies is appropriate since it's used in runtime code (risk-task-notification.ts).

Comment on lines +37 to +38
categoryId: category.id,
requiredArtifactTypes: [],
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Consider removing the empty array initialization for requiredArtifactTypes.

The requiredArtifactTypes is being initialized as an empty array here, but it's already being populated from the requirements in the server action. This initialization might override the server-provided data.

Let's verify if the server-provided requiredArtifactTypes are being overridden:


🏁 Script executed:

#!/bin/bash
# Search for where requiredArtifactTypes is being used or modified
rg -A 5 "requiredArtifactTypes"

Length of output: 14234


Based on the search results, the server action in
apps/app/src/actions/framework/get-framework-categories.ts builds each category’s requiredArtifactTypes by mapping the control’s requirements (i.e.

requiredArtifactTypes: control.requirements?.map((req) => req.type) || [],
```)  
However, in `apps/app/src/components/frameworks/framework-controls.tsx` the property is explicitly set to an empty array:

```js
                categoryId: category.id,
                requiredArtifactTypes: [],

This empty array assignment is indeed overriding any pre-populated values coming from the server. Therefore, the review comment is correct.

Action Required: Remove the empty array assignment so that the server-populated property is preserved.

  • Remove the requiredArtifactTypes: [] line from framework-controls.tsx (or merge it with the already provided data).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants