Split these files that exceeded the 500-line hard cap: - privacy-policy.ts (965 LOC) -> sections + renderers - academy/api.ts (787 LOC) -> courses + mock-data - whistleblower/api.ts (755 LOC) -> operations + mock-data - vvt-profiling.ts (659 LOC) -> data + logic - cookie-banner.ts (595 LOC) -> config + embed - dsr/types.ts (581 LOC) -> core + api types - tom-generator/rules-engine.ts (560 LOC) -> evaluator + gap-analysis - datapoint-helpers.ts (548 LOC) -> generators + validators Each original file becomes a barrel re-export for backward compatibility. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
145 lines
5.5 KiB
TypeScript
145 lines
5.5 KiB
TypeScript
/**
|
|
* Datapoint Helpers — Validation Functions
|
|
*
|
|
* Document validation checks for DSGVO compliance.
|
|
*/
|
|
|
|
import {
|
|
DataPoint,
|
|
LocalizedText,
|
|
SupportedLanguage,
|
|
} from '@/lib/sdk/einwilligungen/types'
|
|
|
|
import type { Language } from './datapoint-generators'
|
|
|
|
// =============================================================================
|
|
// TYPES
|
|
// =============================================================================
|
|
|
|
export interface ValidationWarning {
|
|
type: 'error' | 'warning' | 'info'
|
|
code: string
|
|
message: string
|
|
suggestion: string
|
|
affectedDataPoints?: DataPoint[]
|
|
}
|
|
|
|
// =============================================================================
|
|
// VALIDATION FUNCTIONS
|
|
// =============================================================================
|
|
|
|
export function checkSpecialCategoriesWarning(
|
|
dataPoints: DataPoint[],
|
|
documentContent: string,
|
|
lang: Language = 'de'
|
|
): ValidationWarning | null {
|
|
const specialCategories = dataPoints.filter(dp => dp.isSpecialCategory)
|
|
|
|
if (specialCategories.length === 0) return null
|
|
|
|
const hasSection = lang === 'de'
|
|
? documentContent.includes('Art. 9') || documentContent.includes('Artikel 9') || documentContent.includes('besondere Kategorie')
|
|
: documentContent.includes('Art. 9') || documentContent.includes('Article 9') || documentContent.includes('special categor')
|
|
|
|
if (!hasSection) {
|
|
return {
|
|
type: 'error',
|
|
code: 'MISSING_ART9_SECTION',
|
|
message: lang === 'de'
|
|
? `${specialCategories.length} besondere Datenkategorien (Art. 9 DSGVO) ausgewaehlt, aber kein entsprechender Abschnitt im Dokument gefunden.`
|
|
: `${specialCategories.length} special data categories (Art. 9 GDPR) selected, but no corresponding section found in document.`,
|
|
suggestion: lang === 'de'
|
|
? 'Fuegen Sie einen Abschnitt zu besonderen Kategorien personenbezogener Daten hinzu oder verwenden Sie [BESONDERE_KATEGORIEN] als Platzhalter.'
|
|
: 'Add a section about special categories of personal data or use [BESONDERE_KATEGORIEN] as placeholder.',
|
|
affectedDataPoints: specialCategories
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
export function checkThirdCountryWarning(
|
|
dataPoints: DataPoint[],
|
|
documentContent: string,
|
|
lang: Language = 'de'
|
|
): ValidationWarning | null {
|
|
const thirdCountryIndicators = ['Google', 'AWS', 'Microsoft', 'Meta', 'Facebook', 'Cloudflare', 'USA', 'US']
|
|
|
|
const thirdCountryPoints = dataPoints.filter(dp =>
|
|
dp.thirdPartyRecipients?.some(r =>
|
|
thirdCountryIndicators.some(i => r.toLowerCase().includes(i.toLowerCase()))
|
|
)
|
|
)
|
|
|
|
if (thirdCountryPoints.length === 0) return null
|
|
|
|
const hasSCCMention = lang === 'de'
|
|
? documentContent.includes('Standardvertragsklauseln') || documentContent.includes('SCC') || documentContent.includes('Art. 46')
|
|
: documentContent.includes('Standard Contractual Clauses') || documentContent.includes('SCC') || documentContent.includes('Art. 46')
|
|
|
|
if (!hasSCCMention) {
|
|
return {
|
|
type: 'warning',
|
|
code: 'MISSING_SCC_SECTION',
|
|
message: lang === 'de'
|
|
? `Drittland-Uebermittlung fuer ${thirdCountryPoints.length} Datenpunkte erkannt, aber keine Standardvertragsklauseln (SCC) erwaehnt.`
|
|
: `Third country transfer detected for ${thirdCountryPoints.length} data points, but no Standard Contractual Clauses (SCC) mentioned.`,
|
|
suggestion: lang === 'de'
|
|
? 'Erwaegen Sie die Aufnahme eines Abschnitts zu Drittland-Uebermittlungen und Standardvertragsklauseln oder verwenden Sie [DRITTLAND_TRANSFERS] als Platzhalter.'
|
|
: 'Consider adding a section about third country transfers and Standard Contractual Clauses or use [DRITTLAND_TRANSFERS] as placeholder.',
|
|
affectedDataPoints: thirdCountryPoints
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
export function checkExplicitConsentWarning(
|
|
dataPoints: DataPoint[],
|
|
documentContent: string,
|
|
lang: Language = 'de'
|
|
): ValidationWarning | null {
|
|
const explicitConsentPoints = dataPoints.filter(dp => dp.requiresExplicitConsent)
|
|
|
|
if (explicitConsentPoints.length === 0) return null
|
|
|
|
const hasConsentSection = lang === 'de'
|
|
? documentContent.includes('Einwilligung') || documentContent.includes('Widerruf') || documentContent.includes('Art. 7')
|
|
: documentContent.includes('consent') || documentContent.includes('withdraw') || documentContent.includes('Art. 7')
|
|
|
|
if (!hasConsentSection) {
|
|
return {
|
|
type: 'warning',
|
|
code: 'MISSING_CONSENT_SECTION',
|
|
message: lang === 'de'
|
|
? `${explicitConsentPoints.length} Datenpunkte erfordern ausdrueckliche Einwilligung, aber kein Abschnitt zu Einwilligung/Widerruf gefunden.`
|
|
: `${explicitConsentPoints.length} data points require explicit consent, but no section about consent/withdrawal found.`,
|
|
suggestion: lang === 'de'
|
|
? 'Fuegen Sie einen Abschnitt zum Widerrufsrecht hinzu.'
|
|
: 'Add a section about the right to withdraw consent.',
|
|
affectedDataPoints: explicitConsentPoints
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
export function validateDocument(
|
|
dataPoints: DataPoint[],
|
|
documentContent: string,
|
|
lang: Language = 'de'
|
|
): ValidationWarning[] {
|
|
const warnings: ValidationWarning[] = []
|
|
|
|
const specialCatWarning = checkSpecialCategoriesWarning(dataPoints, documentContent, lang)
|
|
if (specialCatWarning) warnings.push(specialCatWarning)
|
|
|
|
const thirdCountryWarning = checkThirdCountryWarning(dataPoints, documentContent, lang)
|
|
if (thirdCountryWarning) warnings.push(thirdCountryWarning)
|
|
|
|
const consentWarning = checkExplicitConsentWarning(dataPoints, documentContent, lang)
|
|
if (consentWarning) warnings.push(consentWarning)
|
|
|
|
return warnings
|
|
}
|