-
Notifications
You must be signed in to change notification settings - Fork 321
Mariano/stuff #44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mariano/stuff #44
Changes from all commits
d40ab6c
33145ab
710a573
04a7ed7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,58 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "use server"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { authActionClient } from "@/actions/safe-action"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { db, type OrganizationPolicy } from "@bubba/db"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { z } from "zod"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const schema = z.object({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| policyId: z.string(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export type PolicyStatsResponse = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| success: boolean; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data?: OrganizationPolicy[]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const getPolicy = authActionClient | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .schema(schema) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .metadata({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: "get-policy", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| track: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| event: "get-policy", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| channel: "server", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .action(async ({ ctx, parsedInput }) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { user } = ctx; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { policyId } = parsedInput; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!user.organizationId) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| success: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error: "Not authorized - no organization found", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const policy = await db.organizationPolicy.findFirst({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| where: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| organizationId: user.organizationId!, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: policyId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| include: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| policy: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+37
to
+46
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add null check and improve error handling. The database query could be improved with better error handling and null checks. try {
const policy = await db.organizationPolicy.findFirst({
where: {
- organizationId: user.organizationId!,
+ organizationId: user.organizationId,
id: policyId,
},
include: {
policy: true,
},
});
+ if (!policy) {
+ return {
+ success: false,
+ error: "Policy not found",
+ };
+ }📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| success: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data: policy, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| success: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error: "Failed to fetch policy statistics", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,58 +1,13 @@ | ||
| import { auth } from "@/auth"; | ||
| import { PolicyOverview } from "@/components/policies/policy-overview"; | ||
| import { db } from "@bubba/db"; | ||
| import { unstable_cache } from "next/cache"; | ||
| import { redirect } from "next/navigation"; | ||
|
|
||
| interface PageProps { | ||
| params: Promise<{ id: string }>; | ||
| } | ||
|
|
||
| export default async function PolicyPage({ params }: PageProps) { | ||
| const session = await auth(); | ||
| const { id } = await params; | ||
|
|
||
| if (!session) { | ||
| redirect("/login"); | ||
| } | ||
|
|
||
| if (!session.user.organizationId || !id) { | ||
| redirect("/"); | ||
| } | ||
|
|
||
| const policy = await getPolicy(id, session.user.organizationId); | ||
|
|
||
| if (!policy) { | ||
| redirect("/policies"); | ||
| } | ||
|
|
||
| const users = await getUsers(session.user.organizationId); | ||
|
|
||
| return <PolicyOverview policy={policy} />; | ||
| return <PolicyOverview policyId={id} />; | ||
| } | ||
|
|
||
| const getPolicy = unstable_cache( | ||
| async (id: string, organizationId: string) => { | ||
| const policy = await db.artifact.findUnique({ | ||
| where: { | ||
| id, | ||
| type: "policy", | ||
| organizationId: organizationId, | ||
| }, | ||
| }); | ||
|
|
||
| return policy; | ||
| }, | ||
| ["policy-cache"], | ||
| ); | ||
|
|
||
| const getUsers = unstable_cache( | ||
| async (organizationId: string) => { | ||
| const users = await db.user.findMany({ | ||
| where: { organizationId: organizationId }, | ||
| }); | ||
|
|
||
| return users; | ||
| }, | ||
| ["users-cache"], | ||
| ); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| "use client"; | ||
|
|
||
| import { getPolicy } from "../Actions/get-policy"; | ||
| import useSWR from "swr"; | ||
| import type { OrganizationPolicy, Policy } from "@bubba/db"; | ||
|
|
||
| const POLICY_KEY = "policy"; | ||
|
|
||
| type OrganizationPolicyWithPolicy = OrganizationPolicy & { | ||
| policy: Policy; | ||
| }; | ||
|
|
||
| async function fetchPolicy( | ||
| policyId: string | ||
| ): Promise<OrganizationPolicyWithPolicy> { | ||
| const response = await getPolicy({ policyId }); | ||
|
|
||
| if (!response?.data?.success || !response.data.data) { | ||
| throw new Error(response?.data?.error || "Failed to fetch policy data"); | ||
| } | ||
|
|
||
| return response.data.data; | ||
| } | ||
|
|
||
| export function usePolicy({ policyId }: { policyId: string }) { | ||
| const { data, error, isLoading, mutate } = | ||
| useSWR<OrganizationPolicyWithPolicy>( | ||
| [POLICY_KEY, policyId], | ||
| () => fetchPolicy(policyId), | ||
| { | ||
| revalidateOnFocus: false, | ||
| revalidateOnReconnect: false, | ||
| } | ||
| ); | ||
|
|
||
| return { | ||
| data, | ||
| isLoading, | ||
| error, | ||
| mutate, | ||
| }; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,21 @@ | ||
| "use client"; | ||
|
|
||
| import type { Artifact } from "@bubba/db"; | ||
| import type { JSONContent } from "@tiptap/react"; | ||
| import PolicyEditor from "../editor/advanced-editor"; | ||
| import { usePolicy } from "@/app/[locale]/(app)/(dashboard)/policies/hooks/usePolicy"; | ||
|
|
||
| export function PolicyOverview({ policyId }: { policyId: string }) { | ||
| const { data: policy } = usePolicy({ policyId }); | ||
|
|
||
| if (!policy) return null; | ||
|
|
||
| export function PolicyOverview({ policy }: { policy: Artifact }) { | ||
| const content = policy.content as JSONContent; | ||
|
|
||
| if (!content) return null; | ||
|
|
||
| return ( | ||
| <div className="h-[calc(100vh-8rem)] flex flex-col"> | ||
| <PolicyEditor policyId={policy.id} content={content} /> | ||
| <PolicyEditor policyId={policyId} content={content} /> | ||
| </div> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,15 @@ | ||
| { | ||
| "type": "doc", | ||
| "metadata": { | ||
| "controls": ["CC6.1", "CC6.2", "CC6.3"] | ||
| "id": "access-onboarding-and-termination-policy", | ||
| "slug": "access-onboarding-and-termination-policy", | ||
| "name": "Access Onboarding and Termination Policy", | ||
| "description": "This policy outlines the procedures for onboarding and offboarding users to technical infrastructure.", | ||
| "usedBy": { | ||
| "soc2": { | ||
| "CC6": ["CC6.1", "CC6.2", "CC6.3"] | ||
| } | ||
| } | ||
|
Comment on lines
+8
to
+12
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainResolve inconsistencies in SOC2 control mappings. The SOC2 control mappings in this file differ from its duplicate in
Please clarify which file is the source of truth and ensure consistent control mappings across both files. Run the following script to identify all policy files and their SOC2 control mappings: 🏁 Script executed: #!/bin/bash
# Description: Find all policy JSON files and extract their SOC2 control mappings.
# Find all policy JSON files
fd -e json -t f . apps/app/src/jobs/seed/policies packages/data/policies --exec sh -c '
echo "=== $1 ==="
jq -r ".metadata.usedBy.soc2 | if type == \"object\" then to_entries[] | \"\(.key): \(.value | join(\", \"))\" else join(\", \") end" "$1"
echo
' _Length of output: 1410 SOC2 Control Mapping Mismatch – Align Seed Policy with Authoritative Mapping The seed policy in
|
||
| }, | ||
| "content": [ | ||
| { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| { | ||
| "type": "doc", | ||
| "metadata": { | ||
| "controls": ["CC6.2"] | ||
| "controls": ["CC7.1", "CC7.2", "CC7.4"] | ||
| }, | ||
| "content": [ | ||
| { | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -43,5 +43,25 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "name": "CC9: Risk Mitigation", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "code": "CC9", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "description": "This criterion ensures that the organization has a process for mitigating risks to its security posture." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "A1": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "name": "A1: Availability", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "code": "A1", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "description": "This criterion ensures that systems are available for operation and use as committed or agreed." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "C1": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "name": "C1: Data Integrity", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "code": "C1", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "description": "This criterion ensures that data is accurate, complete, and consistent." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "PI1": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "name": "PI1: Processing Integrity", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "code": "PI1", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "description": "This criterion ensures that data is accurate, complete, and consistent." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "P1": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "name": "P1: Privacy", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "code": "P1", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "description": "This criterion ensures that data is accurate, complete, and consistent." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+47
to
66
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update descriptions for C1, PI1, and P1 categories. The descriptions for C1 (Data Integrity), PI1 (Processing Integrity), and P1 (Privacy) are identical. Each category should have a unique description that reflects its specific focus:
Suggested updates: "C1": {
"name": "C1: Data Integrity",
"code": "C1",
- "description": "This criterion ensures that data is accurate, complete, and consistent."
+ "description": "This criterion ensures that stored data maintains its integrity through controls preventing unauthorized modification or corruption."
},
"PI1": {
"name": "PI1: Processing Integrity",
"code": "PI1",
- "description": "This criterion ensures that data is accurate, complete, and consistent."
+ "description": "This criterion ensures that system processing is complete, valid, accurate, timely, and authorized."
},
"P1": {
"name": "P1: Privacy",
"code": "P1",
- "description": "This criterion ensures that data is accurate, complete, and consistent."
+ "description": "This criterion ensures that personal information is collected, used, retained, disclosed, and disposed of in accordance with privacy commitments and requirements."
}📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the response type definition.
The
PolicyStatsResponsetype'sdataproperty should be a singleOrganizationPolicyorundefined, not an array, as thefindFirstquery returns a single record.export type PolicyStatsResponse = { success: boolean; - data?: OrganizationPolicy[]; + data?: OrganizationPolicy; error?: string; };📝 Committable suggestion