Skip to content

Commit 0e7492c

Browse files
github-actions[bot]chasprowebdevcarhartlewis
authored
feat(app): add SOC 3
* feat(db): new migration to add fields for SOC 3 * fix(api): support soc2 for trust-portal endpoints * fix(app): add SOC 3 to Trust portal frameworks list --------- Co-authored-by: chasprowebdev <chasgarciaprowebdev@gmail.com> Co-authored-by: Lewis Carhart <lewis@trycomp.ai>
1 parent 0a2d766 commit 0e7492c

8 files changed

Lines changed: 90 additions & 0 deletions

File tree

apps/api/src/trust-portal/trust-access.service.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1911,6 +1911,7 @@ export class TrustAccessService {
19111911
| 'iso42001'
19121912
| 'gdpr'
19131913
| 'hipaa'
1914+
| 'soc3'
19141915
| 'soc2type1'
19151916
| 'soc2type2'
19161917
| 'pci_dss'
@@ -1923,6 +1924,7 @@ export class TrustAccessService {
19231924
[TrustFramework.hipaa]: 'hipaa',
19241925
[TrustFramework.soc2_type1]: 'soc2type1',
19251926
[TrustFramework.soc2_type2]: 'soc2type2',
1927+
[TrustFramework.soc3]: 'soc3',
19261928
[TrustFramework.pci_dss]: 'pci_dss',
19271929
[TrustFramework.nen_7510]: 'nen7510',
19281930
[TrustFramework.iso_9001]: 'iso9001',
@@ -2594,6 +2596,8 @@ export class TrustAccessService {
25942596
const CERT_MAP: Record<string, string> = {
25952597
soc2: 'soc2',
25962598
'soc 2': 'soc2',
2599+
soc3: 'soc3',
2600+
'soc 3': 'soc3',
25972601
iso27001: 'iso27001',
25982602
'iso 27001': 'iso27001',
25992603
iso42001: 'iso42001',
@@ -2647,6 +2651,7 @@ export class TrustAccessService {
26472651

26482652
const LABEL_MAP: Record<string, string> = {
26492653
soc2: 'SOC 2',
2654+
soc3: 'SOC 3',
26502655
iso27001: 'ISO 27001',
26512656
iso42001: 'ISO 42001',
26522657
iso9001: 'ISO 9001',

apps/api/src/trust-portal/trust-portal.service.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ export class TrustPortalService {
123123
| 'iso42001_status'
124124
| 'gdpr_status'
125125
| 'hipaa_status'
126+
| 'soc3_status'
126127
| 'soc2type1_status'
127128
| 'soc2type2_status'
128129
| 'pci_dss_status'
@@ -133,6 +134,7 @@ export class TrustPortalService {
133134
| 'iso42001'
134135
| 'gdpr'
135136
| 'hipaa'
137+
| 'soc3'
136138
| 'soc2type1'
137139
| 'soc2type2'
138140
| 'pci_dss'
@@ -186,6 +188,11 @@ export class TrustPortalService {
186188
enabledField: 'iso9001',
187189
slug: 'iso_9001',
188190
},
191+
[TrustFramework.soc3]: {
192+
statusField: 'soc3_status',
193+
enabledField: 'soc3',
194+
slug: 'soc3',
195+
},
189196
};
190197

191198
async getDomainStatus(
@@ -612,6 +619,7 @@ export class TrustPortalService {
612619
soc2: 'soc2',
613620
soc2type1: 'soc2type1',
614621
soc2type2: 'soc2type2',
622+
soc3: 'soc3',
615623
iso27001: 'iso27001',
616624
iso42001: 'iso42001',
617625
gdpr: 'gdpr',
@@ -626,6 +634,7 @@ export class TrustPortalService {
626634
const statusFieldMap: Record<string, string> = {
627635
soc2type1Status: 'soc2type1_status',
628636
soc2type2Status: 'soc2type2_status',
637+
soc3Status: 'soc3_status',
629638
iso27001Status: 'iso27001_status',
630639
iso42001Status: 'iso42001_status',
631640
gdprStatus: 'gdpr_status',
@@ -636,6 +645,7 @@ export class TrustPortalService {
636645
// Also support snake_case input (from other callers)
637646
soc2type1_status: 'soc2type1_status',
638647
soc2type2_status: 'soc2type2_status',
648+
soc3_status: 'soc3_status',
639649
iso27001_status: 'iso27001_status',
640650
iso42001_status: 'iso42001_status',
641651
gdpr_status: 'gdpr_status',
@@ -1515,6 +1525,7 @@ export class TrustPortalService {
15151525
// Framework flags
15161526
soc2type1: trust.soc2type1 ?? false,
15171527
soc2type2: trust.soc2type2 || trust.soc2 || false,
1528+
soc3: trust.soc3 ?? false,
15181529
iso27001: trust.iso27001 ?? false,
15191530
iso42001: trust.iso42001 ?? false,
15201531
gdpr: trust.gdpr ?? false,
@@ -1528,6 +1539,7 @@ export class TrustPortalService {
15281539
!trust.soc2type2 && trust.soc2
15291540
? (trust.soc2_status ?? 'started')
15301541
: (trust.soc2type2_status ?? 'started'),
1542+
soc3Status: trust.soc3_status ?? 'started',
15311543
iso27001Status: trust.iso27001_status ?? 'started',
15321544
iso42001Status: trust.iso42001_status ?? 'started',
15331545
gdprStatus: trust.gdpr_status ?? 'started',

apps/app/src/app/(app)/[orgId]/trust/page.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export default async function TrustPage({
4343
hipaa: 'hipaaFileName',
4444
soc2_type1: 'soc2type1FileName',
4545
soc2_type2: 'soc2type2FileName',
46+
soc3: 'soc3FileName',
4647
pci_dss: 'pcidssFileName',
4748
nen_7510: 'nen7510FileName',
4849
iso_9001: 'iso9001FileName',
@@ -55,6 +56,7 @@ export default async function TrustPage({
5556
hipaaFileName: null,
5657
soc2type1FileName: null,
5758
soc2type2FileName: null,
59+
soc3FileName: null,
5860
pcidssFileName: null,
5961
nen7510FileName: null,
6062
iso9001FileName: null,
@@ -100,6 +102,7 @@ export default async function TrustPage({
100102
primaryColor={settings?.primaryColor ?? null}
101103
soc2type1={settings?.soc2type1 ?? false}
102104
soc2type2={settings?.soc2type2 ?? false}
105+
soc3={settings?.soc3 ?? false}
103106
iso27001={settings?.iso27001 ?? false}
104107
iso42001={settings?.iso42001 ?? false}
105108
gdpr={settings?.gdpr ?? false}
@@ -109,6 +112,7 @@ export default async function TrustPage({
109112
iso9001={settings?.iso9001 ?? false}
110113
soc2type1Status={settings?.soc2type1Status ?? 'started'}
111114
soc2type2Status={settings?.soc2type2Status ?? 'started'}
115+
soc3Status={settings?.soc3Status ?? 'started'}
112116
iso27001Status={settings?.iso27001Status ?? 'started'}
113117
iso42001Status={settings?.iso42001Status ?? 'started'}
114118
gdprStatus={settings?.gdprStatus ?? 'started'}
@@ -123,6 +127,7 @@ export default async function TrustPage({
123127
hipaaFileName={certificateFiles.hipaaFileName}
124128
soc2type1FileName={certificateFiles.soc2type1FileName}
125129
soc2type2FileName={certificateFiles.soc2type2FileName}
130+
soc3FileName={certificateFiles.soc3FileName}
126131
pcidssFileName={certificateFiles.pcidssFileName}
127132
nen7510FileName={certificateFiles.nen7510FileName}
128133
iso9001FileName={certificateFiles.iso9001FileName}

apps/app/src/app/(app)/[orgId]/trust/portal-settings/components/TrustPortalSwitch.test.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ describe('TrustPortalSwitch permission gating', () => {
200200
orgId: 'org-1',
201201
soc2type1: false,
202202
soc2type2: false,
203+
soc3: false,
203204
iso27001: true,
204205
iso42001: false,
205206
gdpr: false,
@@ -208,6 +209,7 @@ describe('TrustPortalSwitch permission gating', () => {
208209
nen7510: false,
209210
soc2type1Status: 'started' as const,
210211
soc2type2Status: 'started' as const,
212+
soc3Status: 'started' as const,
211213
iso27001Status: 'compliant' as const,
212214
iso42001Status: 'started' as const,
213215
gdprStatus: 'started' as const,

apps/app/src/app/(app)/[orgId]/trust/portal-settings/components/TrustPortalSwitch.tsx

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ const trustPortalSwitchSchema = z.object({
6161
primaryColor: z.string().optional(),
6262
soc2type1: z.boolean(),
6363
soc2type2: z.boolean(),
64+
soc3: z.boolean(),
6465
iso27001: z.boolean(),
6566
iso42001: z.boolean(),
6667
gdpr: z.boolean(),
@@ -70,6 +71,7 @@ const trustPortalSwitchSchema = z.object({
7071
iso9001: z.boolean(),
7172
soc2type1Status: z.enum(['started', 'in_progress', 'compliant']),
7273
soc2type2Status: z.enum(['started', 'in_progress', 'compliant']),
74+
soc3Status: z.enum(['started', 'in_progress', 'compliant']),
7375
iso27001Status: z.enum(['started', 'in_progress', 'compliant']),
7476
iso42001Status: z.enum(['started', 'in_progress', 'compliant']),
7577
gdprStatus: z.enum(['started', 'in_progress', 'compliant']),
@@ -93,6 +95,7 @@ const FRAMEWORK_KEY_TO_API_SLUG: Record<string, string> = {
9395
hipaa: 'hipaa',
9496
soc2type1: 'soc2_type1',
9597
soc2type2: 'soc2_type2',
98+
soc3: 'soc3',
9699
pcidss: 'pci_dss',
97100
nen7510: 'nen_7510',
98101
iso9001: 'iso_9001',
@@ -151,13 +154,15 @@ export function TrustPortalSwitch({
151154
orgId,
152155
soc2type1,
153156
soc2type2,
157+
soc3,
154158
iso27001,
155159
iso42001,
156160
gdpr,
157161
hipaa,
158162
pcidss,
159163
soc2type1Status,
160164
soc2type2Status,
165+
soc3Status,
161166
iso27001Status,
162167
iso42001Status,
163168
gdprStatus,
@@ -174,6 +179,7 @@ export function TrustPortalSwitch({
174179
hipaaFileName,
175180
soc2type1FileName,
176181
soc2type2FileName,
182+
soc3FileName,
177183
pcidssFileName,
178184
nen7510FileName,
179185
iso9001FileName,
@@ -192,6 +198,7 @@ export function TrustPortalSwitch({
192198
orgId: string;
193199
soc2type1: boolean;
194200
soc2type2: boolean;
201+
soc3: boolean;
195202
iso27001: boolean;
196203
iso42001: boolean;
197204
gdpr: boolean;
@@ -200,6 +207,7 @@ export function TrustPortalSwitch({
200207
nen7510: boolean;
201208
soc2type1Status: 'started' | 'in_progress' | 'compliant';
202209
soc2type2Status: 'started' | 'in_progress' | 'compliant';
210+
soc3Status: 'started' | 'in_progress' | 'compliant';
203211
iso27001Status: 'started' | 'in_progress' | 'compliant';
204212
iso42001Status: 'started' | 'in_progress' | 'compliant';
205213
gdprStatus: 'started' | 'in_progress' | 'compliant';
@@ -215,6 +223,7 @@ export function TrustPortalSwitch({
215223
hipaaFileName?: string | null;
216224
soc2type1FileName?: string | null;
217225
soc2type2FileName?: string | null;
226+
soc3FileName?: string | null;
218227
pcidssFileName?: string | null;
219228
nen7510FileName?: string | null;
220229
iso9001FileName?: string | null;
@@ -240,6 +249,7 @@ export function TrustPortalSwitch({
240249
hipaa: hipaaFileName ?? null,
241250
soc2type1: soc2type1FileName ?? null,
242251
soc2type2: soc2type2FileName ?? null,
252+
soc3: soc3FileName ?? null,
243253
pcidss: pcidssFileName ?? null,
244254
nen7510: nen7510FileName ?? null,
245255
iso9001: iso9001FileName ?? null,
@@ -253,6 +263,7 @@ export function TrustPortalSwitch({
253263
hipaa: hipaaFileName ?? null,
254264
soc2type1: soc2type1FileName ?? null,
255265
soc2type2: soc2type2FileName ?? null,
266+
soc3: soc3FileName ?? null,
256267
pcidss: pcidssFileName ?? null,
257268
nen7510: nen7510FileName ?? null,
258269
iso9001: iso9001FileName ?? null,
@@ -264,6 +275,7 @@ export function TrustPortalSwitch({
264275
hipaaFileName,
265276
soc2type1FileName,
266277
soc2type2FileName,
278+
soc3FileName,
267279
pcidssFileName,
268280
nen7510FileName,
269281
iso9001FileName,
@@ -323,6 +335,7 @@ export function TrustPortalSwitch({
323335
primaryColor: primaryColor ?? undefined,
324336
soc2type1: soc2type1 ?? false,
325337
soc2type2: soc2type2 ?? false,
338+
soc3: soc3 ?? false,
326339
iso27001: iso27001 ?? false,
327340
iso42001: iso42001 ?? false,
328341
gdpr: gdpr ?? false,
@@ -332,6 +345,7 @@ export function TrustPortalSwitch({
332345
iso9001: iso9001 ?? false,
333346
soc2type1Status: soc2type1Status ?? 'started',
334347
soc2type2Status: soc2type2Status ?? 'started',
348+
soc3Status: soc3Status ?? 'started',
335349
iso27001Status: iso27001Status ?? 'started',
336350
iso42001Status: iso42001Status ?? 'started',
337351
gdprStatus: gdprStatus ?? 'started',
@@ -685,6 +699,39 @@ export function TrustPortalSwitch({
685699
orgId={orgId}
686700
disabled={!canUpdate}
687701
/>
702+
{/* SOC 3 */}
703+
<ComplianceFramework
704+
title="SOC 3"
705+
description="A compliance framework focused on data security, availability, and confidentiality."
706+
isEnabled={soc3}
707+
status={soc3Status}
708+
onStatusChange={async (value) => {
709+
try {
710+
await updateFrameworkSettings({
711+
soc3Status: value as 'started' | 'in_progress' | 'compliant',
712+
});
713+
toast.success('SOC 3 status updated');
714+
} catch (error) {
715+
toast.error('Failed to update SOC 3 status');
716+
}
717+
}}
718+
onToggle={async (checked) => {
719+
try {
720+
await updateFrameworkSettings({
721+
soc3: checked,
722+
});
723+
toast.success('SOC 3 status updated');
724+
} catch (error) {
725+
toast.error('Failed to update SOC 3 status');
726+
}
727+
}}
728+
fileName={certificateFiles.soc3}
729+
onFileUpload={handleFileUpload}
730+
onFilePreview={handleFilePreview}
731+
frameworkKey="soc3"
732+
orgId={orgId}
733+
disabled={!canUpdate}
734+
/>
688735
{/* PCI DSS */}
689736
<ComplianceFramework
690737
title="PCI DSS"
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
-- Add SOC 3 support to the trust portal:
2+
-- * new `soc3` value on the `TrustFramework` enum (used by TrustResource)
3+
-- * new `soc3` and `soc3_status` columns on the `Trust` model
4+
--
5+
-- Safe to combine these in a single migration because `soc3_status` uses the
6+
-- pre-existing `FrameworkStatus` enum — the newly-added `TrustFramework.soc3`
7+
-- value is not referenced in any statement in this file.
8+
ALTER TYPE "TrustFramework" ADD VALUE IF NOT EXISTS 'soc3';
9+
10+
ALTER TABLE "Trust"
11+
ADD COLUMN "soc3" BOOLEAN NOT NULL DEFAULT false,
12+
ADD COLUMN "soc3_status" "FrameworkStatus" NOT NULL DEFAULT 'started';

packages/db/prisma/schema/trust.prisma

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ model Trust {
1717
soc2 Boolean @default(false)
1818
soc2type1 Boolean @default(false)
1919
soc2type2 Boolean @default(false)
20+
soc3 Boolean @default(false)
2021
iso27001 Boolean @default(false)
2122
iso42001 Boolean @default(false)
2223
nen7510 Boolean @default(false)
@@ -28,6 +29,7 @@ model Trust {
2829
soc2_status FrameworkStatus @default(started)
2930
soc2type1_status FrameworkStatus @default(started)
3031
soc2type2_status FrameworkStatus @default(started)
32+
soc3_status FrameworkStatus @default(started)
3133
iso27001_status FrameworkStatus @default(started)
3234
iso42001_status FrameworkStatus @default(started)
3335
nen7510_status FrameworkStatus @default(started)
@@ -68,6 +70,7 @@ enum TrustFramework {
6870
hipaa
6971
soc2_type1
7072
soc2_type2
73+
soc3
7174
pci_dss
7275
nen_7510
7376
iso_9001

packages/docs/openapi.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12022,6 +12022,7 @@
1202212022
"hipaa",
1202312023
"soc2_type1",
1202412024
"soc2_type2",
12025+
"soc3",
1202512026
"pci_dss",
1202612027
"nen_7510",
1202712028
"iso_9001"
@@ -23568,6 +23569,7 @@
2356823569
"hipaa",
2356923570
"soc2_type1",
2357023571
"soc2_type2",
23572+
"soc3",
2357123573
"pci_dss",
2357223574
"nen_7510",
2357323575
"iso_9001"
@@ -23609,6 +23611,7 @@
2360923611
"hipaa",
2361023612
"soc2_type1",
2361123613
"soc2_type2",
23614+
"soc3",
2361223615
"pci_dss",
2361323616
"nen_7510",
2361423617
"iso_9001"
@@ -23651,6 +23654,7 @@
2365123654
"hipaa",
2365223655
"soc2_type1",
2365323656
"soc2_type2",
23657+
"soc3",
2365423658
"pci_dss",
2365523659
"nen_7510",
2365623660
"iso_9001"

0 commit comments

Comments
 (0)