Skip to content

Commit 65f4b87

Browse files
authored
Merge branch 'main' into feat/github-2fa-check
2 parents 47e78bf + 6a0397a commit 65f4b87

File tree

25 files changed

+807
-119
lines changed

25 files changed

+807
-119
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
## [3.21.2](https://github.com/trycompai/comp/compare/v3.21.1...v3.21.2) (2026-04-13)
2+
3+
4+
### Bug Fixes
5+
6+
* **trust:** update PCI DSS trust portal badge icon ([d2e7fdf](https://github.com/trycompai/comp/commit/d2e7fdfb5bf21c42141250fadb15c8ccc5aa6bc9))
7+
8+
## [3.21.1](https://github.com/trycompai/comp/compare/v3.21.0...v3.21.1) (2026-04-13)
9+
10+
11+
### Bug Fixes
12+
13+
* **frameworks:** add PCI DSS Level 1 badge mapping ([a5a7f80](https://github.com/trycompai/comp/commit/a5a7f80db5fa9eea161e81ec0f30ca9fdcd9cd11))
14+
* **frameworks:** support PCI DSS badge name variants ([24fe953](https://github.com/trycompai/comp/commit/24fe953974779f7a985a69aa731df094754dea4a))
15+
116
# [3.21.0](https://github.com/trycompai/comp/compare/v3.20.2...v3.21.0) (2026-04-10)
217

318

apps/api/src/cloud-security/cloud-security-query.service.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export interface CloudProvider {
1717
status: string;
1818
createdAt: Date;
1919
updatedAt: Date;
20+
reconnectedAt?: Date;
2021
isLegacy: boolean;
2122
variables: Record<string, unknown> | null;
2223
requiredVariables: string[];
@@ -96,6 +97,12 @@ export class CloudSecurityQueryService {
9697
const newProviders: CloudProvider[] = newConnections.map((conn) => {
9798
const metadata = (conn.metadata || {}) as Record<string, unknown>;
9899
const manifest = getManifest(conn.provider.slug);
100+
const reconnectMarker = metadata.reconnectedAt;
101+
const reconnectedAt =
102+
typeof reconnectMarker === 'string' &&
103+
!Number.isNaN(new Date(reconnectMarker).getTime())
104+
? new Date(reconnectMarker)
105+
: undefined;
99106
return {
100107
id: conn.id,
101108
integrationId: conn.provider.slug,
@@ -109,6 +116,7 @@ export class CloudSecurityQueryService {
109116
status: conn.status,
110117
createdAt: conn.createdAt,
111118
updatedAt: conn.updatedAt,
119+
reconnectedAt,
112120
isLegacy: false,
113121
variables: (conn.variables as Record<string, unknown>) ?? null,
114122
requiredVariables: getRequiredVariables(conn.provider.slug),

apps/api/src/integration-platform/controllers/oauth.controller.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,23 @@ export class OAuthController {
315315

316316
// Store tokens and mark connection as active
317317
await this.credentialVaultService.storeOAuthTokens(connection.id, tokens);
318+
319+
// Mark cloud OAuth reconnect completion so reconnect banners clear after successful OAuth.
320+
if (manifest.category === 'Cloud') {
321+
const metadata =
322+
connection.metadata &&
323+
typeof connection.metadata === 'object' &&
324+
!Array.isArray(connection.metadata)
325+
? (connection.metadata as Record<string, unknown>)
326+
: {};
327+
connection = await this.connectionRepository.update(connection.id, {
328+
metadata: {
329+
...metadata,
330+
reconnectedAt: new Date().toISOString(),
331+
},
332+
});
333+
}
334+
318335
await this.connectionService.activateConnection(connection.id);
319336

320337
// Provider-specific post-OAuth actions

apps/api/src/integration-platform/repositories/connection.repository.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@ export class ConnectionRepository {
3939
organizationId: string,
4040
): Promise<IntegrationConnection | null> {
4141
return db.integrationConnection.findFirst({
42-
where: { providerId, organizationId },
42+
where: {
43+
providerId,
44+
organizationId,
45+
status: { not: 'disconnected' },
46+
},
4347
orderBy: { createdAt: 'desc' },
4448
include: {
4549
provider: true,

apps/app/src/app/(app)/[orgId]/cloud-tests/components/ProviderTabs.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useApi } from '@/hooks/use-api';
22
import { useConnectionServices } from '@/hooks/use-integration-platform';
3+
import { CLOUD_RECONNECT_CUTOFF_LABEL } from '@/lib/cloud-reconnect-policy';
34
import { Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Tabs, TabsContent, TabsList, TabsTrigger } from '@trycompai/design-system';
45
import { Add } from '@trycompai/design-system/icons';
56
import { useCallback, useEffect, useRef, useState } from 'react';
@@ -24,6 +25,7 @@ interface ProviderTabsProps {
2425
onAddConnection: (providerType: string) => void;
2526
onConfigure: (provider: Provider) => void;
2627
needsConfiguration: (provider: Provider) => boolean;
28+
requiresReconnect: (provider: Provider) => boolean;
2729
canRunScan?: boolean;
2830
canAddConnection?: boolean;
2931
orgId: string;
@@ -218,6 +220,7 @@ export function ProviderTabs({
218220
onAddConnection,
219221
onConfigure,
220222
needsConfiguration,
223+
requiresReconnect,
221224
canRunScan,
222225
canAddConnection,
223226
orgId,
@@ -293,9 +296,29 @@ export function ProviderTabs({
293296
</div>
294297

295298
{connections.map((connection) => {
299+
const reconnectRequired = requiresReconnect(connection);
300+
296301
return (
297302
<TabsContent key={connection.id} value={connection.id}>
298303
<div className="mt-4">
304+
{reconnectRequired && (
305+
<div className="mb-4 rounded-lg border border-warning/30 bg-warning/10 px-4 py-3">
306+
<div className="flex items-center justify-between gap-3">
307+
<div>
308+
<p className="text-sm font-medium">Reconnect this account</p>
309+
<p className="text-xs text-muted-foreground mt-0.5">
310+
This connection was created before {CLOUD_RECONNECT_CUTOFF_LABEL}. Reconnect it to keep scans and remediation fully reliable.
311+
</p>
312+
</div>
313+
{canAddConnection !== false && (
314+
<Button size="sm" onClick={() => onAddConnection(providerType)}>
315+
Reconnect
316+
</Button>
317+
)}
318+
</div>
319+
</div>
320+
)}
321+
299322
<ConnectionDetails connection={connection} />
300323

301324
{/* New platform connections get full tabbed UI */}

apps/app/src/app/(app)/[orgId]/cloud-tests/components/TestsLayout.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { ConnectIntegrationDialog } from '@/components/integrations/ConnectInteg
44
import { useApi } from '@/hooks/use-api';
55
import { usePermissions } from '@/hooks/use-permissions';
66
import { ManageIntegrationDialog } from '@/components/integrations/ManageIntegrationDialog';
7+
import { CLOUD_RECONNECT_CUTOFF_LABEL, requiresCloudReconnect } from '@/lib/cloud-reconnect-policy';
78
import { Button, PageHeader, PageHeaderDescription, PageLayout } from '@trycompai/design-system';
89
import { Add, Settings } from '@trycompai/design-system/icons';
910
import { useSearchParams, useRouter } from 'next/navigation';
@@ -101,6 +102,19 @@ export function TestsLayout({ initialFindings, initialProviders, orgId }: TestsL
101102
const isProvidersValidating = providersResponse.isValidating;
102103

103104
const connectedProviders = providers;
105+
const reconnectRequiredCount = useMemo(
106+
() =>
107+
connectedProviders.filter((provider) =>
108+
requiresCloudReconnect({
109+
providerId: provider.integrationId,
110+
createdAt: provider.createdAt,
111+
reconnectedAt: provider.reconnectedAt,
112+
isLegacy: provider.isLegacy,
113+
status: provider.status,
114+
}),
115+
).length,
116+
[connectedProviders],
117+
);
104118

105119
// Group connections by provider type (aws, gcp, azure)
106120
const providerGroups = useMemo(() => {
@@ -269,6 +283,17 @@ export function TestsLayout({ initialFindings, initialProviders, orgId }: TestsL
269283
<PageHeaderDescription>{multiProviderDescription}</PageHeaderDescription>
270284
</PageHeader>
271285

286+
{reconnectRequiredCount > 0 && (
287+
<div className="mb-4 rounded-lg border border-warning/30 bg-warning/10 px-4 py-3">
288+
<p className="text-sm font-medium text-foreground">
289+
Reconnect required for {reconnectRequiredCount} cloud connection{reconnectRequiredCount === 1 ? '' : 's'}
290+
</p>
291+
<p className="text-xs text-muted-foreground mt-0.5">
292+
Connections created before {CLOUD_RECONNECT_CUTOFF_LABEL} should be re-added to keep scans and remediation fully reliable.
293+
</p>
294+
</div>
295+
)}
296+
272297
<ProviderTabs
273298
providerGroups={providerGroups}
274299
providerTypes={activeProviderTypes}
@@ -303,6 +328,15 @@ export function TestsLayout({ initialFindings, initialProviders, orgId }: TestsL
303328
setConfigureDialogOpen(true);
304329
}}
305330
needsConfiguration={needsVariableConfiguration}
331+
requiresReconnect={(provider) =>
332+
requiresCloudReconnect({
333+
providerId: provider.integrationId,
334+
createdAt: provider.createdAt,
335+
reconnectedAt: provider.reconnectedAt,
336+
isLegacy: provider.isLegacy,
337+
status: provider.status,
338+
})
339+
}
306340
canRunScan={canRunScan}
307341
canAddConnection={canCreateIntegration}
308342
orgId={orgId}

apps/app/src/app/(app)/[orgId]/cloud-tests/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export interface Provider {
2323
status: string;
2424
createdAt: Date;
2525
updatedAt: Date;
26+
reconnectedAt?: Date | string | null;
2627
isLegacy?: boolean;
2728
variables?: Record<string, unknown> | null;
2829
requiredVariables?: string[];
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { PageLayout } from '@trycompai/design-system';
2+
3+
export default function Loading() {
4+
return <PageLayout loading />;
5+
}

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

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { CompanyFormPageClient } from '@/app/(app)/[orgId]/documents/components/
22
import { Breadcrumb, PageLayout } from '@trycompai/design-system';
33
import Link from 'next/link';
44
import { notFound } from 'next/navigation';
5-
import { Suspense } from 'react';
65
import { evidenceFormDefinitions, evidenceFormTypeSchema } from '../forms';
76
import { auth } from '@/utils/auth';
87
import { headers } from 'next/headers';
@@ -43,13 +42,11 @@ export default async function CompanyFormDetailPage({
4342
{ label: formDefinition.title, isCurrent: true },
4443
]}
4544
/>
46-
<Suspense>
47-
<CompanyFormPageClient
48-
organizationId={orgId}
49-
formType={parsedType.data}
50-
isPlatformAdmin={isPlatformAdmin}
51-
/>
52-
</Suspense>
45+
<CompanyFormPageClient
46+
organizationId={orgId}
47+
formType={parsedType.data}
48+
isPlatformAdmin={isPlatformAdmin}
49+
/>
5350
</PageLayout>
5451
);
5552
}

apps/app/src/app/(app)/[orgId]/documents/components/CompanyFormPageClient.tsx

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -426,19 +426,7 @@ export function CompanyFormPageClient({
426426
</InputGroup>
427427
</div>
428428

429-
{isLoading ? (
430-
<Empty>
431-
<EmptyMedia variant="icon">
432-
<Catalog />
433-
</EmptyMedia>
434-
<EmptyHeader>
435-
<EmptyTitle>No submissions yet</EmptyTitle>
436-
<EmptyDescription>
437-
Start by creating a new submission, click the New Submission button above.
438-
</EmptyDescription>
439-
</EmptyHeader>
440-
</Empty>
441-
) : !data || data.submissions.length === 0 ? (
429+
{!data || data.submissions.length === 0 ? (
442430
<Empty>
443431
<EmptyMedia variant="icon">
444432
<Catalog />

0 commit comments

Comments
 (0)