Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions apps/api/src/trust-portal/trust-portal.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { db } from '@db';
import { TrustPortalService } from './trust-portal.service';

jest.mock('@db', () => ({
db: {
vendor: {
findMany: jest.fn(),
update: jest.fn(),
},
globalVendors: {
findUnique: jest.fn(),
},
},
Prisma: {},
TrustFramework: {
iso_27001: 'iso_27001',
iso_42001: 'iso_42001',
gdpr: 'gdpr',
hipaa: 'hipaa',
soc2_type1: 'soc2_type1',
soc2_type2: 'soc2_type2',
pci_dss: 'pci_dss',
nen_7510: 'nen_7510',
iso_9001: 'iso_9001',
},
}));

jest.mock('../app/s3', () => ({
APP_AWS_ORG_ASSETS_BUCKET: 'org-assets',
s3Client: { send: jest.fn() },
getSignedUrl: jest.fn(),
}));

const mockDb = db as unknown as {
vendor: { findMany: jest.Mock; update: jest.Mock };
globalVendors: { findUnique: jest.Mock };
};

describe('TrustPortalService getAllVendorsWithSync compliance badges', () => {
const service = new TrustPortalService();

beforeEach(() => {
jest.clearAllMocks();
});

// Regression: Scaleway's GlobalVendors record lists "ISO/IEC 27001:2022",
// "HDS" and "GDPR Compliance" as verified, but the Trust Centre only showed
// GDPR. The "IEC" infix and ":2022" suffix caused the ISO 27001 certification
// to be dropped during badge sync, understating the vendor's posture.
it('maps "ISO/IEC 27001:2022" to the iso27001 badge alongside gdpr', async () => {
const baseVendor = {
id: 'vnd_scaleway',
name: 'Scaleway',
description: null,
website: 'scaleway.com',
showOnTrustPortal: true,
logoUrl: 'https://logo.example/scaleway.png',
complianceBadges: null,
trustPortalOrder: 0,
};

mockDb.vendor.findMany.mockResolvedValue([baseVendor]);
mockDb.globalVendors.findUnique.mockResolvedValue({
riskAssessmentData: {
certifications: [
{ type: 'ISO/IEC 27001:2022', status: 'verified' },
{ type: 'HDS', status: 'verified' },
{ type: 'GDPR Compliance', status: 'verified' },
],
},
});
mockDb.vendor.update.mockImplementation(
async ({ data }: { data: Record<string, unknown> }) => ({
...baseVendor,
...data,
}),
);

const result = await service.getAllVendorsWithSync('org_1');

const badgeTypes = (
result[0].complianceBadges as unknown as Array<{ type: string }>
).map((badge) => badge.type);

expect(badgeTypes).toContain('iso27001');
expect(badgeTypes).toContain('gdpr');
});
});
12 changes: 6 additions & 6 deletions apps/api/src/trust-portal/trust-portal.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1950,10 +1950,11 @@ export class TrustPortalService {

if (normalized.includes('soc2') || normalized.includes('soc 2'))
return 'soc2';
if (normalized.includes('iso27001') || normalized.includes('iso 27001'))
return 'iso27001';
if (normalized.includes('iso42001') || normalized.includes('iso 42001'))
return 'iso42001';
// Match ISO standards by their number. Vendors write these many ways
// ("ISO 27001", "ISO/IEC 27001:2022"), and the "IEC" infix breaks a naive
// includes('iso27001') check ("iso27001" is not a substring of "isoiec27001…").
if (normalized.includes('27001')) return 'iso27001';
if (normalized.includes('42001')) return 'iso42001';
if (normalized.includes('gdpr')) return 'gdpr';
if (normalized.includes('hipaa')) return 'hipaa';
if (
Expand All @@ -1964,8 +1965,7 @@ export class TrustPortalService {
return 'pci_dss';
if (normalized.includes('nen7510') || normalized.includes('nen 7510'))
return 'nen7510';
if (normalized.includes('iso9001') || normalized.includes('iso 9001'))
return 'iso9001';
if (normalized.includes('9001')) return 'iso9001';

return null;
}
Expand Down
Loading