@tantainnovative/ndpr-toolkit 1.0.5 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +447 -84
- package/dist/index.esm.js +2 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +35 -0
- package/package.json +52 -70
- package/CHANGELOG.md +0 -16
- package/CNAME +0 -1
- package/CONTRIBUTING.md +0 -87
- package/RELEASE-NOTES-v1.0.0.md +0 -140
- package/RELEASE-NOTES-v1.0.1.md +0 -69
- package/SECURITY.md +0 -21
- package/components.json +0 -21
- package/eslint.config.mjs +0 -16
- package/next-env.d.ts +0 -5
- package/next.config.js +0 -15
- package/next.config.ts +0 -62
- package/packages/ndpr-toolkit/README.md +0 -467
- package/packages/ndpr-toolkit/dist/index.esm.js +0 -2
- package/packages/ndpr-toolkit/dist/index.esm.js.map +0 -1
- package/packages/ndpr-toolkit/dist/index.js +0 -2
- package/packages/ndpr-toolkit/dist/index.js.map +0 -1
- package/packages/ndpr-toolkit/package-lock.json +0 -8197
- package/packages/ndpr-toolkit/package.json +0 -71
- package/packages/ndpr-toolkit/rollup.config.js +0 -34
- package/packages/ndpr-toolkit/src/components/breach/BreachNotificationManager.tsx +0 -701
- package/packages/ndpr-toolkit/src/components/breach/BreachReportForm.tsx +0 -631
- package/packages/ndpr-toolkit/src/components/breach/BreachRiskAssessment.tsx +0 -569
- package/packages/ndpr-toolkit/src/components/breach/RegulatoryReportGenerator.tsx +0 -496
- package/packages/ndpr-toolkit/src/components/consent/ConsentBanner.tsx +0 -270
- package/packages/ndpr-toolkit/src/components/consent/ConsentManager.tsx +0 -217
- package/packages/ndpr-toolkit/src/components/consent/ConsentStorage.tsx +0 -206
- package/packages/ndpr-toolkit/src/components/dpia/DPIAQuestionnaire.tsx +0 -342
- package/packages/ndpr-toolkit/src/components/dpia/DPIAReport.tsx +0 -373
- package/packages/ndpr-toolkit/src/components/dpia/StepIndicator.tsx +0 -174
- package/packages/ndpr-toolkit/src/components/dsr/DSRDashboard.tsx +0 -717
- package/packages/ndpr-toolkit/src/components/dsr/DSRRequestForm.tsx +0 -476
- package/packages/ndpr-toolkit/src/components/dsr/DSRTracker.tsx +0 -620
- package/packages/ndpr-toolkit/src/components/policy/PolicyExporter.tsx +0 -541
- package/packages/ndpr-toolkit/src/components/policy/PolicyGenerator.tsx +0 -454
- package/packages/ndpr-toolkit/src/components/policy/PolicyPreview.tsx +0 -333
- package/packages/ndpr-toolkit/src/hooks/useBreach.ts +0 -409
- package/packages/ndpr-toolkit/src/hooks/useConsent.ts +0 -263
- package/packages/ndpr-toolkit/src/hooks/useDPIA.ts +0 -457
- package/packages/ndpr-toolkit/src/hooks/useDSR.ts +0 -236
- package/packages/ndpr-toolkit/src/hooks/usePrivacyPolicy.ts +0 -428
- package/packages/ndpr-toolkit/src/index.ts +0 -44
- package/packages/ndpr-toolkit/src/setupTests.ts +0 -5
- package/packages/ndpr-toolkit/src/types/breach.ts +0 -283
- package/packages/ndpr-toolkit/src/types/consent.ts +0 -111
- package/packages/ndpr-toolkit/src/types/dpia.ts +0 -236
- package/packages/ndpr-toolkit/src/types/dsr.ts +0 -192
- package/packages/ndpr-toolkit/src/types/index.ts +0 -42
- package/packages/ndpr-toolkit/src/types/privacy.ts +0 -246
- package/packages/ndpr-toolkit/src/utils/breach.ts +0 -122
- package/packages/ndpr-toolkit/src/utils/consent.ts +0 -51
- package/packages/ndpr-toolkit/src/utils/dpia.ts +0 -104
- package/packages/ndpr-toolkit/src/utils/dsr.ts +0 -77
- package/packages/ndpr-toolkit/src/utils/privacy.ts +0 -100
- package/packages/ndpr-toolkit/tsconfig.json +0 -23
- package/postcss.config.mjs +0 -5
- package/public/NDPR TOOLKIT.svg +0 -1
- package/public/favicon/android-chrome-192x192.png +0 -0
- package/public/favicon/android-chrome-512x512.png +0 -0
- package/public/favicon/apple-touch-icon.png +0 -0
- package/public/favicon/favicon-16x16.png +0 -0
- package/public/favicon/favicon-32x32.png +0 -0
- package/public/favicon/site.webmanifest +0 -1
- package/public/file.svg +0 -1
- package/public/globe.svg +0 -1
- package/public/ndpr-toolkit-logo.svg +0 -108
- package/public/next.svg +0 -1
- package/public/vercel.svg +0 -1
- package/public/window.svg +0 -1
- package/src/app/accessibility.css +0 -70
- package/src/app/favicon.ico +0 -0
- package/src/app/globals.css +0 -123
- package/src/app/layout.tsx +0 -37
- package/src/app/ndpr-demos/breach/page.tsx +0 -354
- package/src/app/ndpr-demos/consent/page.tsx +0 -366
- package/src/app/ndpr-demos/dpia/page.tsx +0 -495
- package/src/app/ndpr-demos/dsr/page.tsx +0 -280
- package/src/app/ndpr-demos/page.tsx +0 -73
- package/src/app/ndpr-demos/policy/page.tsx +0 -771
- package/src/app/page.tsx +0 -452
- package/src/components/ErrorBoundary.tsx +0 -90
- package/src/components/breach-notification/BreachNotificationForm.tsx +0 -479
- package/src/components/consent/ConsentBanner.tsx +0 -193
- package/src/components/data-subject-rights/DataSubjectRequestForm.tsx +0 -530
- package/src/components/dpia/DPIAQuestionnaire.tsx +0 -523
- package/src/components/privacy-policy/PolicyGenerator.tsx +0 -1062
- package/src/components/privacy-policy/data.ts +0 -98
- package/src/components/privacy-policy/shared/CheckboxField.tsx +0 -38
- package/src/components/privacy-policy/shared/CheckboxGroup.tsx +0 -85
- package/src/components/privacy-policy/shared/FormField.tsx +0 -79
- package/src/components/privacy-policy/shared/StepIndicator.tsx +0 -86
- package/src/components/privacy-policy/steps/CustomSectionsStep.tsx +0 -361
- package/src/components/privacy-policy/steps/DataCollectionStep.tsx +0 -231
- package/src/components/privacy-policy/steps/DataSharingStep.tsx +0 -418
- package/src/components/privacy-policy/steps/OrganizationInfoStep.tsx +0 -202
- package/src/components/privacy-policy/steps/PolicyPreviewStep.tsx +0 -226
- package/src/components/ui/Badge.tsx +0 -46
- package/src/components/ui/Button.tsx +0 -59
- package/src/components/ui/Card.tsx +0 -92
- package/src/components/ui/Checkbox.tsx +0 -57
- package/src/components/ui/FormField.tsx +0 -50
- package/src/components/ui/Input.tsx +0 -38
- package/src/components/ui/Loading.tsx +0 -201
- package/src/components/ui/Select.tsx +0 -42
- package/src/components/ui/TextArea.tsx +0 -38
- package/src/components/ui/label.tsx +0 -24
- package/src/components/ui/switch.tsx +0 -31
- package/src/components/ui/tabs.tsx +0 -66
- package/src/hooks/useConsent.ts +0 -70
- package/src/hooks/useLoadingState.ts +0 -85
- package/src/lib/consentService.ts +0 -144
- package/src/lib/dpiaQuestions.ts +0 -188
- package/src/lib/requestService.ts +0 -79
- package/src/lib/sanitize.ts +0 -108
- package/src/lib/storage.ts +0 -222
- package/src/lib/utils.ts +0 -6
- package/src/types/html-to-docx.d.ts +0 -30
- package/src/types/index.ts +0 -77
- package/tailwind.config.ts +0 -65
- package/tsconfig.json +0 -41
- /package/{packages/ndpr-toolkit/dist → dist}/components/breach/BreachNotificationManager.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/components/breach/BreachReportForm.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/components/breach/BreachRiskAssessment.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/components/breach/RegulatoryReportGenerator.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/components/consent/ConsentBanner.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/components/consent/ConsentManager.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/components/consent/ConsentStorage.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/components/dpia/DPIAQuestionnaire.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/components/dpia/DPIAReport.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/components/dpia/StepIndicator.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/components/dsr/DSRDashboard.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/components/dsr/DSRRequestForm.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/components/dsr/DSRTracker.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/components/policy/PolicyExporter.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/components/policy/PolicyGenerator.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/components/policy/PolicyPreview.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/hooks/useBreach.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/hooks/useConsent.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/hooks/useDPIA.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/hooks/useDSR.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/hooks/usePrivacyPolicy.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/index.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/setupTests.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/types/breach.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/types/consent.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/types/dpia.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/types/dsr.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/types/privacy.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/utils/breach.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/utils/consent.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/utils/dpia.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/utils/dsr.d.ts +0 -0
- /package/{packages/ndpr-toolkit/dist → dist}/utils/privacy.d.ts +0 -0
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { ConsentRecord, ConsentHistoryEntry } from "@/types";
|
|
4
|
-
import { v4 as uuidv4 } from "uuid";
|
|
5
|
-
import { storage } from "./storage";
|
|
6
|
-
|
|
7
|
-
// In a real implementation, this would connect to a database
|
|
8
|
-
// For demo purposes, we're using localStorage
|
|
9
|
-
|
|
10
|
-
const CONSENT_STORAGE_KEY = "ndpr_consent_records";
|
|
11
|
-
const CONSENT_HISTORY_KEY = "ndpr_consent_history";
|
|
12
|
-
|
|
13
|
-
// Helper function to get consent history
|
|
14
|
-
const getConsentHistoryHelper = (): ConsentHistoryEntry[] => {
|
|
15
|
-
return storage.getItem<ConsentHistoryEntry[]>(CONSENT_HISTORY_KEY, []) || [];
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export const consentService = {
|
|
19
|
-
// Save a new consent record
|
|
20
|
-
saveConsent: (
|
|
21
|
-
consents: Record<string, boolean>,
|
|
22
|
-
userId?: string,
|
|
23
|
-
): ConsentRecord => {
|
|
24
|
-
const consentRecord: ConsentRecord = {
|
|
25
|
-
id: uuidv4(),
|
|
26
|
-
userId,
|
|
27
|
-
consents,
|
|
28
|
-
timestamp: new Date(),
|
|
29
|
-
ipAddress: "Collected server-side in real implementation",
|
|
30
|
-
userAgent:
|
|
31
|
-
typeof window !== "undefined" ? window.navigator.userAgent : "Unknown",
|
|
32
|
-
version: "1.0",
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
// Store in localStorage for demo
|
|
36
|
-
// Save as current consent
|
|
37
|
-
if (!storage.setItem(CONSENT_STORAGE_KEY, consentRecord)) {
|
|
38
|
-
throw new Error("Failed to save consent record");
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Add to history
|
|
42
|
-
const historyEntry: ConsentHistoryEntry = {
|
|
43
|
-
timestamp: new Date(),
|
|
44
|
-
consents,
|
|
45
|
-
action: "granted",
|
|
46
|
-
ipAddress: "Collected server-side in real implementation",
|
|
47
|
-
userAgent:
|
|
48
|
-
typeof window !== "undefined" ? window.navigator.userAgent : "Unknown",
|
|
49
|
-
version: "1.0",
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
const history = getConsentHistoryHelper();
|
|
53
|
-
history.push(historyEntry);
|
|
54
|
-
if (!storage.setItem(CONSENT_HISTORY_KEY, history)) {
|
|
55
|
-
throw new Error("Failed to save consent history");
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return consentRecord;
|
|
59
|
-
},
|
|
60
|
-
|
|
61
|
-
// Get the current consent record
|
|
62
|
-
getCurrentConsent: (): ConsentRecord | null => {
|
|
63
|
-
return storage.getItem<ConsentRecord>(CONSENT_STORAGE_KEY);
|
|
64
|
-
},
|
|
65
|
-
|
|
66
|
-
// Get consent history
|
|
67
|
-
getConsentHistory: (): ConsentHistoryEntry[] => {
|
|
68
|
-
return getConsentHistoryHelper();
|
|
69
|
-
},
|
|
70
|
-
|
|
71
|
-
// Update consent with change reason
|
|
72
|
-
updateConsent: (
|
|
73
|
-
consents: Record<string, boolean>,
|
|
74
|
-
changeReason?: string,
|
|
75
|
-
userId?: string,
|
|
76
|
-
): ConsentRecord => {
|
|
77
|
-
// Get the previous consent to determine the action
|
|
78
|
-
const previousConsent = consentService.getCurrentConsent();
|
|
79
|
-
|
|
80
|
-
// Create the new consent record
|
|
81
|
-
const consentRecord: ConsentRecord = {
|
|
82
|
-
id: uuidv4(),
|
|
83
|
-
userId,
|
|
84
|
-
consents,
|
|
85
|
-
timestamp: new Date(),
|
|
86
|
-
ipAddress: "Collected server-side in real implementation",
|
|
87
|
-
userAgent:
|
|
88
|
-
typeof window !== "undefined" ? window.navigator.userAgent : "Unknown",
|
|
89
|
-
version: "1.0",
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
// Store in localStorage for demo
|
|
93
|
-
// Save as current consent
|
|
94
|
-
if (!storage.setItem(CONSENT_STORAGE_KEY, consentRecord)) {
|
|
95
|
-
throw new Error("Failed to save consent record");
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Determine the action type
|
|
99
|
-
let action: "granted" | "revoked" | "updated" = "updated";
|
|
100
|
-
if (!previousConsent) {
|
|
101
|
-
action = "granted";
|
|
102
|
-
} else {
|
|
103
|
-
const allRevoked = Object.values(consents).every((v) => !v);
|
|
104
|
-
if (allRevoked) {
|
|
105
|
-
action = "revoked";
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Add to history
|
|
110
|
-
const historyEntry: ConsentHistoryEntry = {
|
|
111
|
-
timestamp: new Date(),
|
|
112
|
-
consents,
|
|
113
|
-
action,
|
|
114
|
-
ipAddress: "Collected server-side in real implementation",
|
|
115
|
-
userAgent:
|
|
116
|
-
typeof window !== "undefined" ? window.navigator.userAgent : "Unknown",
|
|
117
|
-
version: "1.0",
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const history = getConsentHistoryHelper();
|
|
121
|
-
history.push(historyEntry);
|
|
122
|
-
if (!storage.setItem(CONSENT_HISTORY_KEY, history)) {
|
|
123
|
-
throw new Error("Failed to save consent history");
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return consentRecord;
|
|
127
|
-
},
|
|
128
|
-
|
|
129
|
-
// Clear all consent data (for testing/development)
|
|
130
|
-
clearConsentData: (): boolean => {
|
|
131
|
-
const clearedCurrent = storage.removeItem(CONSENT_STORAGE_KEY);
|
|
132
|
-
const clearedHistory = storage.removeItem(CONSENT_HISTORY_KEY);
|
|
133
|
-
return clearedCurrent && clearedHistory;
|
|
134
|
-
},
|
|
135
|
-
|
|
136
|
-
// Check if a specific consent is granted
|
|
137
|
-
hasConsent: (type: string): boolean => {
|
|
138
|
-
const current = consentService.getCurrentConsent();
|
|
139
|
-
if (!current) return false;
|
|
140
|
-
return current.consents[type] === true;
|
|
141
|
-
},
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
export default consentService;
|
package/src/lib/dpiaQuestions.ts
DELETED
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
import { RiskAssessmentQuestion } from "@/types";
|
|
2
|
-
|
|
3
|
-
export const dpiaQuestions: RiskAssessmentQuestion[] = [
|
|
4
|
-
{
|
|
5
|
-
id: "data-collection-1",
|
|
6
|
-
text: "Does your project involve collecting personal data directly from individuals?",
|
|
7
|
-
type: "radio",
|
|
8
|
-
required: true,
|
|
9
|
-
options: [
|
|
10
|
-
{ value: "1", label: "No personal data is collected" },
|
|
11
|
-
{
|
|
12
|
-
value: "2",
|
|
13
|
-
label: "Limited personal data is collected with clear consent",
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
value: "3",
|
|
17
|
-
label: "Significant personal data is collected with consent",
|
|
18
|
-
},
|
|
19
|
-
{ value: "4", label: "Extensive personal data is collected" },
|
|
20
|
-
],
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
id: "data-collection-2",
|
|
24
|
-
text: "Does your project involve collecting sensitive personal data (e.g., health, biometric, religious beliefs)?",
|
|
25
|
-
type: "radio",
|
|
26
|
-
required: true,
|
|
27
|
-
options: [
|
|
28
|
-
{ value: "1", label: "No sensitive data is collected" },
|
|
29
|
-
{ value: "2", label: "Limited sensitive data with explicit consent" },
|
|
30
|
-
{ value: "3", label: "Significant sensitive data with explicit consent" },
|
|
31
|
-
{ value: "4", label: "Extensive sensitive data collection" },
|
|
32
|
-
],
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
id: "data-collection-3",
|
|
36
|
-
text: "Does your project collect data from children or vulnerable individuals?",
|
|
37
|
-
type: "radio",
|
|
38
|
-
required: true,
|
|
39
|
-
options: [
|
|
40
|
-
{ value: "1", label: "No data from children or vulnerable individuals" },
|
|
41
|
-
{ value: "2", label: "Limited data with parental/guardian consent" },
|
|
42
|
-
{ value: "3", label: "Significant data with enhanced safeguards" },
|
|
43
|
-
{ value: "4", label: "Extensive data from vulnerable groups" },
|
|
44
|
-
],
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
id: "data-processing-1",
|
|
48
|
-
text: "Does your project involve automated decision-making or profiling?",
|
|
49
|
-
type: "radio",
|
|
50
|
-
required: true,
|
|
51
|
-
options: [
|
|
52
|
-
{ value: "1", label: "No automated decision-making" },
|
|
53
|
-
{ value: "2", label: "Limited automation with human oversight" },
|
|
54
|
-
{ value: "3", label: "Significant automation with opt-out options" },
|
|
55
|
-
{
|
|
56
|
-
value: "4",
|
|
57
|
-
label: "Extensive automated decisions affecting individuals",
|
|
58
|
-
},
|
|
59
|
-
],
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
id: "data-processing-2",
|
|
63
|
-
text: "Does your project involve processing data for purposes beyond what was initially collected for?",
|
|
64
|
-
type: "radio",
|
|
65
|
-
required: true,
|
|
66
|
-
options: [
|
|
67
|
-
{ value: "1", label: "Processing only for original purpose" },
|
|
68
|
-
{ value: "2", label: "Limited secondary processing with notice" },
|
|
69
|
-
{ value: "3", label: "Significant secondary processing with consent" },
|
|
70
|
-
{ value: "4", label: "Extensive repurposing of data" },
|
|
71
|
-
],
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
id: "data-processing-3",
|
|
75
|
-
text: "Does your project combine data from multiple sources?",
|
|
76
|
-
type: "radio",
|
|
77
|
-
required: true,
|
|
78
|
-
options: [
|
|
79
|
-
{ value: "1", label: "No data combination" },
|
|
80
|
-
{ value: "2", label: "Limited combination from controlled sources" },
|
|
81
|
-
{ value: "3", label: "Significant combination with transparency" },
|
|
82
|
-
{ value: "4", label: "Extensive data aggregation from various sources" },
|
|
83
|
-
],
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
id: "data-sharing-1",
|
|
87
|
-
text: "Does your project involve sharing personal data with third parties?",
|
|
88
|
-
type: "radio",
|
|
89
|
-
required: true,
|
|
90
|
-
options: [
|
|
91
|
-
{ value: "1", label: "No third-party sharing" },
|
|
92
|
-
{ value: "2", label: "Limited sharing with contractual safeguards" },
|
|
93
|
-
{
|
|
94
|
-
value: "3",
|
|
95
|
-
label: "Significant sharing with data processing agreements",
|
|
96
|
-
},
|
|
97
|
-
{ value: "4", label: "Extensive sharing with multiple third parties" },
|
|
98
|
-
],
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
id: "data-sharing-2",
|
|
102
|
-
text: "Does your project involve transferring data outside Nigeria?",
|
|
103
|
-
type: "radio",
|
|
104
|
-
required: true,
|
|
105
|
-
options: [
|
|
106
|
-
{ value: "1", label: "No international transfers" },
|
|
107
|
-
{
|
|
108
|
-
value: "2",
|
|
109
|
-
label: "Limited transfers to countries with adequate protection",
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
value: "3",
|
|
113
|
-
label: "Significant transfers with standard contractual clauses",
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
value: "4",
|
|
117
|
-
label: "Extensive transfers to countries without adequate protection",
|
|
118
|
-
},
|
|
119
|
-
],
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
id: "security-1",
|
|
123
|
-
text: "What level of security measures does your project implement?",
|
|
124
|
-
type: "radio",
|
|
125
|
-
required: true,
|
|
126
|
-
options: [
|
|
127
|
-
{
|
|
128
|
-
value: "1",
|
|
129
|
-
label:
|
|
130
|
-
"Comprehensive security with encryption, access controls, and regular audits",
|
|
131
|
-
},
|
|
132
|
-
{ value: "2", label: "Strong security measures with some monitoring" },
|
|
133
|
-
{
|
|
134
|
-
value: "3",
|
|
135
|
-
label: "Basic security measures meeting minimum requirements",
|
|
136
|
-
},
|
|
137
|
-
{ value: "4", label: "Limited security measures" },
|
|
138
|
-
],
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
id: "security-2",
|
|
142
|
-
text: "Does your project have a data breach response plan?",
|
|
143
|
-
type: "radio",
|
|
144
|
-
required: true,
|
|
145
|
-
options: [
|
|
146
|
-
{ value: "1", label: "Comprehensive breach plan with regular testing" },
|
|
147
|
-
{
|
|
148
|
-
value: "2",
|
|
149
|
-
label: "Documented breach plan with assigned responsibilities",
|
|
150
|
-
},
|
|
151
|
-
{ value: "3", label: "Basic breach notification procedure" },
|
|
152
|
-
{ value: "4", label: "No formal breach response plan" },
|
|
153
|
-
],
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
id: "data-subject-rights-1",
|
|
157
|
-
text: "How does your project facilitate data subject rights (access, rectification, erasure, etc.)?",
|
|
158
|
-
type: "radio",
|
|
159
|
-
required: true,
|
|
160
|
-
options: [
|
|
161
|
-
{ value: "1", label: "Automated self-service portal for all rights" },
|
|
162
|
-
{
|
|
163
|
-
value: "2",
|
|
164
|
-
label: "Documented procedures with reasonable response times",
|
|
165
|
-
},
|
|
166
|
-
{ value: "3", label: "Basic manual process for handling requests" },
|
|
167
|
-
{ value: "4", label: "Limited or no formal process for rights requests" },
|
|
168
|
-
],
|
|
169
|
-
},
|
|
170
|
-
{
|
|
171
|
-
id: "data-subject-rights-2",
|
|
172
|
-
text: "Does your project provide clear privacy information to data subjects?",
|
|
173
|
-
type: "radio",
|
|
174
|
-
required: true,
|
|
175
|
-
options: [
|
|
176
|
-
{
|
|
177
|
-
value: "1",
|
|
178
|
-
label: "Comprehensive, layered privacy notices in plain language",
|
|
179
|
-
},
|
|
180
|
-
{
|
|
181
|
-
value: "2",
|
|
182
|
-
label: "Clear privacy policy with all required information",
|
|
183
|
-
},
|
|
184
|
-
{ value: "3", label: "Basic privacy notice covering essential elements" },
|
|
185
|
-
{ value: "4", label: "Minimal or complex privacy information" },
|
|
186
|
-
],
|
|
187
|
-
},
|
|
188
|
-
];
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { DataSubjectRequest, RequestStatus } from "@/types";
|
|
4
|
-
import { v4 as uuidv4 } from "uuid";
|
|
5
|
-
import { storage } from "./storage";
|
|
6
|
-
|
|
7
|
-
const REQUEST_STORAGE_KEY = "ndpr_requests";
|
|
8
|
-
|
|
9
|
-
const getStoredRequests = (): DataSubjectRequest[] => {
|
|
10
|
-
return storage.getItem<DataSubjectRequest[]>(REQUEST_STORAGE_KEY, []) || [];
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export const requestService = {
|
|
14
|
-
createRequest: (
|
|
15
|
-
requestType: DataSubjectRequest["type"],
|
|
16
|
-
requesterName: string,
|
|
17
|
-
requesterEmail: string,
|
|
18
|
-
details: string,
|
|
19
|
-
): DataSubjectRequest => {
|
|
20
|
-
const request: DataSubjectRequest = {
|
|
21
|
-
id: uuidv4(),
|
|
22
|
-
type: requestType,
|
|
23
|
-
status: "pending",
|
|
24
|
-
createdAt: Date.now(),
|
|
25
|
-
updatedAt: Date.now(),
|
|
26
|
-
subject: {
|
|
27
|
-
name: requesterName,
|
|
28
|
-
email: requesterEmail,
|
|
29
|
-
},
|
|
30
|
-
description: details,
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const requests = getStoredRequests();
|
|
34
|
-
requests.push(request);
|
|
35
|
-
const saved = storage.setItem(REQUEST_STORAGE_KEY, requests);
|
|
36
|
-
|
|
37
|
-
if (!saved) {
|
|
38
|
-
throw new Error(
|
|
39
|
-
"Failed to save request. Storage may be full or unavailable.",
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return request;
|
|
44
|
-
},
|
|
45
|
-
|
|
46
|
-
updateStatus: (
|
|
47
|
-
id: string,
|
|
48
|
-
status: RequestStatus,
|
|
49
|
-
): DataSubjectRequest | null => {
|
|
50
|
-
const requests = getStoredRequests();
|
|
51
|
-
const idx = requests.findIndex((r) => r.id === id);
|
|
52
|
-
if (idx === -1) return null;
|
|
53
|
-
|
|
54
|
-
requests[idx].status = status;
|
|
55
|
-
requests[idx].updatedAt = Date.now();
|
|
56
|
-
|
|
57
|
-
const saved = storage.setItem(REQUEST_STORAGE_KEY, requests);
|
|
58
|
-
if (!saved) {
|
|
59
|
-
throw new Error("Failed to update request status.");
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return requests[idx];
|
|
63
|
-
},
|
|
64
|
-
|
|
65
|
-
getRequest: (id: string): DataSubjectRequest | null => {
|
|
66
|
-
const requests = getStoredRequests();
|
|
67
|
-
return requests.find((r) => r.id === id) || null;
|
|
68
|
-
},
|
|
69
|
-
|
|
70
|
-
getAllRequests: (): DataSubjectRequest[] => {
|
|
71
|
-
return getStoredRequests();
|
|
72
|
-
},
|
|
73
|
-
|
|
74
|
-
clear: (): boolean => {
|
|
75
|
-
return storage.removeItem(REQUEST_STORAGE_KEY);
|
|
76
|
-
},
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
export default requestService;
|
package/src/lib/sanitize.ts
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sanitization utilities for preventing XSS attacks
|
|
3
|
-
* In production, consider using a library like DOMPurify
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Escapes HTML special characters to prevent XSS
|
|
8
|
-
*/
|
|
9
|
-
export function escapeHtml(unsafe: string): string {
|
|
10
|
-
return unsafe
|
|
11
|
-
.replace(/&/g, "&")
|
|
12
|
-
.replace(/</g, "<")
|
|
13
|
-
.replace(/>/g, ">")
|
|
14
|
-
.replace(/"/g, """)
|
|
15
|
-
.replace(/'/g, "'");
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Sanitizes user input for safe display
|
|
20
|
-
* Removes dangerous tags and attributes
|
|
21
|
-
*/
|
|
22
|
-
export function sanitizeInput(input: string): string {
|
|
23
|
-
// Remove script tags and their content
|
|
24
|
-
let sanitized = input.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
|
|
25
|
-
|
|
26
|
-
// Remove on* event handlers
|
|
27
|
-
sanitized = sanitized.replace(/\s*on\w+\s*=\s*["'][^"']*["']/gi, '');
|
|
28
|
-
|
|
29
|
-
// Remove javascript: protocol
|
|
30
|
-
sanitized = sanitized.replace(/javascript:/gi, '');
|
|
31
|
-
|
|
32
|
-
// Escape remaining HTML
|
|
33
|
-
return escapeHtml(sanitized);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Validates and sanitizes email addresses
|
|
38
|
-
*/
|
|
39
|
-
export function sanitizeEmail(email: string): string {
|
|
40
|
-
// Basic email validation
|
|
41
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
42
|
-
|
|
43
|
-
if (!emailRegex.test(email)) {
|
|
44
|
-
throw new Error('Invalid email format');
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Remove any HTML tags
|
|
48
|
-
return email.replace(/<[^>]*>/g, '').trim().toLowerCase();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Sanitizes file names to prevent directory traversal attacks
|
|
53
|
-
*/
|
|
54
|
-
export function sanitizeFileName(fileName: string): string {
|
|
55
|
-
// Remove path separators and null bytes
|
|
56
|
-
return fileName
|
|
57
|
-
.replace(/[\/\\]/g, '')
|
|
58
|
-
.replace(/\0/g, '')
|
|
59
|
-
.replace(/\.{2,}/g, '.')
|
|
60
|
-
.trim();
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Creates a safe HTML structure from markdown
|
|
65
|
-
* This is a basic implementation - for production use a proper markdown parser
|
|
66
|
-
*/
|
|
67
|
-
export function markdownToSafeHtml(markdown: string): string {
|
|
68
|
-
let html = escapeHtml(markdown);
|
|
69
|
-
|
|
70
|
-
// Convert markdown syntax to HTML
|
|
71
|
-
html = html
|
|
72
|
-
// Headers
|
|
73
|
-
.replace(/^### (.*$)/gim, '<h3>$1</h3>')
|
|
74
|
-
.replace(/^## (.*$)/gim, '<h2>$1</h2>')
|
|
75
|
-
.replace(/^# (.*$)/gim, '<h1>$1</h1>')
|
|
76
|
-
// Bold
|
|
77
|
-
.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
|
|
78
|
-
// Italic
|
|
79
|
-
.replace(/\*([^*]+)\*/g, '<em>$1</em>')
|
|
80
|
-
// Line breaks
|
|
81
|
-
.replace(/\n/g, '<br />');
|
|
82
|
-
|
|
83
|
-
return html;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Validates and sanitizes URL to prevent open redirect vulnerabilities
|
|
88
|
-
*/
|
|
89
|
-
export function sanitizeUrl(url: string, allowedHosts?: string[]): string {
|
|
90
|
-
try {
|
|
91
|
-
const parsed = new URL(url);
|
|
92
|
-
|
|
93
|
-
// Check if protocol is safe
|
|
94
|
-
if (!['http:', 'https:'].includes(parsed.protocol)) {
|
|
95
|
-
throw new Error('Invalid protocol');
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Check if host is allowed
|
|
99
|
-
if (allowedHosts && !allowedHosts.includes(parsed.hostname)) {
|
|
100
|
-
throw new Error('Host not allowed');
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return parsed.toString();
|
|
104
|
-
} catch {
|
|
105
|
-
// If URL parsing fails, return a safe default
|
|
106
|
-
return '#';
|
|
107
|
-
}
|
|
108
|
-
}
|