Skip to content

Commit dd1dd96

Browse files
committed
Improve GDPR compliance of my-data json download
1 parent a198d92 commit dd1dd96

7 files changed

Lines changed: 106 additions & 23 deletions

File tree

_TODO.md

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,6 @@ https://aws.plainenglish.io/how-to-build-a-chatbot-using-aws-lex-and-lambda-in-2
2323
- Moving the scroll bar up quickly with the mouse seems to make the header logic break - the Switcher component and Breadcrumbs are hidden under the header
2424
- Themepicker and search icon are too big in non-squished header. Logo too - the initial presentation should be smaller.
2525

26-
## GDPR Compliance for "My Data"
27-
28-
**Weaknesses: Missing Information for Right of Access (Article 15)**
29-
30-
The Right of Access is broader than just a data dump. A complete response must include specific supplementary information that your JSON currently lacks:
31-
32-
- Retention Periods: You must state how long you intend to keep each category of data.
33-
- Third-Party Recipients: You must list any recipients or categories of recipients (like email service providers or analytics tools) with whom this data has been shared.
34-
- Data Subject Rights: The response should remind the user of their other rights, such as the right to request erasure, rectification, or to lodge a complaint with a supervisory authority.
35-
- Source of Data: While your JSON shows "source," it should explicitly confirm if any data was collected from third parties rather than the subject themselves.
36-
37-
**Technical Observations**
38-
39-
- Privacy Policy Version: Your output shows "privacyPolicyVersion": "1970-01-01". This looks like a placeholder or a Unix epoch default; ensure this reflects the actual policy version the user agreed to for each record to maintain a valid audit trail.
40-
- Consent Text: Most records show "consentText": null. GDPR requires you to be able to demonstrate what the subject consented to; providing the actual text shown to the user at the time is a best practice for documenting valid consent.
41-
42-
Next Step: To be fully compliant, you should pair this JSON file with a summary document (often a PDF or HTML page) that includes the missing legal disclosures (retention, recipients, and rights) or include those fields directly in your JSON schema.
43-
4426
## Resume
4527

4628
- Finish styling

src/actions/contact/action.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ export const contact = {
125125
userAgent,
126126
ipAddress: ip !== 'unknown' ? ip : null,
127127
privacyPolicyVersion: getPrivacyPolicyVersion(),
128-
consentText: null,
128+
consentText:
129+
'I consent to Webstack Builders processing my personal data for responding to your inquiry. See our Privacy Policy and Cookie Policy.',
129130
verified: true,
130131
})
131132
}

src/actions/downloads/action.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ export const downloads = {
6060
userAgent,
6161
ipAddress: ip !== 'unknown' ? ip : null,
6262
privacyPolicyVersion: getPrivacyPolicyVersion(),
63-
consentText: null,
63+
consentText:
64+
'I consent to Webstack Builders processing my personal data for providing your requested download. See our Privacy Policy and Cookie Policy.',
6465
verified: true,
6566
})
6667
}

src/actions/gdpr/entities/dsar.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,103 @@ export async function verifyDsarToken(token: string): Promise<DsarVerifyResult>
113113
createdAt:
114114
record.createdAt instanceof Date ? record.createdAt.toISOString() : record.createdAt,
115115
})),
116+
legalDisclosures: {
117+
retentionPeriods: {
118+
consentRecords:
119+
'Retained for 7 years from the date of consent to meet legal compliance and audit requirements.',
120+
contactFormSubmissions:
121+
'Retained for 3 years from the date of submission for legitimate business correspondence purposes.',
122+
newsletterSubscriptions:
123+
'Retained until you unsubscribe, plus 12 months thereafter for audit purposes.',
124+
downloadRecords:
125+
'Retained for 2 years from the date of download for business records purposes.',
126+
},
127+
thirdPartyRecipients: [
128+
{
129+
name: 'Resend',
130+
category: 'Email delivery service',
131+
purpose:
132+
'Delivers transactional and marketing emails on our behalf. Receives your email address and name.',
133+
privacyPolicy: 'https://resend.com/privacy',
134+
},
135+
{
136+
name: 'HubSpot',
137+
category: 'Customer relationship management (CRM) and marketing platform',
138+
purpose:
139+
'Stores contact details and newsletter subscription status for marketing communications. Receives your email address and name when you subscribe to the newsletter or submit the contact form with marketing consent.',
140+
privacyPolicy: 'https://legal.hubspot.com/privacy-policy',
141+
},
142+
{
143+
name: 'Upstash',
144+
category: 'Rate-limiting and caching service',
145+
purpose:
146+
'Stores anonymised request fingerprints to enforce rate limits. Does not receive personally identifiable information.',
147+
privacyPolicy: 'https://upstash.com/privacy',
148+
},
149+
{
150+
name: 'Sentry',
151+
category: 'Error monitoring service',
152+
purpose:
153+
'Captures application error reports which may include request metadata. Configured to scrub personally identifiable information from error payloads.',
154+
privacyPolicy: 'https://sentry.io/privacy/',
155+
},
156+
{
157+
name: 'Vercel',
158+
category: 'Hosting and infrastructure provider',
159+
purpose:
160+
'Hosts and serves this website. All web requests are processed through Vercel infrastructure.',
161+
privacyPolicy: 'https://vercel.com/legal/privacy-policy',
162+
},
163+
],
164+
yourRights: {
165+
summary:
166+
'Under the UK GDPR and EU GDPR you have the following rights regarding your personal data.',
167+
rights: [
168+
{
169+
article: 'Article 15',
170+
right: 'Right of access',
171+
description:
172+
'You have the right to obtain a copy of the personal data we hold about you and supplementary information about how it is processed.',
173+
},
174+
{
175+
article: 'Article 16',
176+
right: 'Right to rectification',
177+
description:
178+
'You have the right to request correction of inaccurate personal data we hold about you.',
179+
},
180+
{
181+
article: 'Article 17',
182+
right: 'Right to erasure',
183+
description:
184+
"You have the right to request deletion of your personal data ('right to be forgotten'). Use the Delete My Data form at https://www.webstackbuilders.com/privacy/my-data to submit a deletion request.",
185+
},
186+
{
187+
article: 'Article 18',
188+
right: 'Right to restriction of processing',
189+
description:
190+
'You have the right to request that we restrict the processing of your personal data in certain circumstances.',
191+
},
192+
{
193+
article: 'Article 20',
194+
right: 'Right to data portability',
195+
description:
196+
'You have the right to receive your personal data in a structured, commonly used, machine-readable format (such as this JSON file) and to transmit it to another controller.',
197+
},
198+
{
199+
article: 'Article 21',
200+
right: 'Right to object',
201+
description:
202+
'You have the right to object to the processing of your personal data for direct marketing purposes at any time.',
203+
},
204+
],
205+
howToExercise:
206+
'To exercise any of these rights, contact us at privacy@webstackbuilders.com. We will respond within 30 days.',
207+
supervisoryAuthority:
208+
'You have the right to lodge a complaint with a supervisory authority. In the UK this is the Information Commissioner\'s Office (ICO): https://ico.org.uk. In the EU, contact the supervisory authority in your country of residence.',
209+
},
210+
dataSource:
211+
'All personal data in this export was collected directly from you through first-party form submissions on webstackbuilders.com. No data has been obtained from third-party sources.',
212+
},
116213
}
117214

118215
return {

src/actions/newsletter/action.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,8 @@ export const newsletter = {
156156
? context.clientAddress
157157
: null,
158158
privacyPolicyVersion: getPrivacyPolicyVersion(),
159-
consentText: null,
159+
consentText:
160+
'I consent to Webstack Builders processing my personal data for marketing communications (unsubscribe anytime). See our Privacy Policy and Cookie Policy.',
160161
verified: false,
161162
})
162163

-4.94 MB
Binary file not shown.

src/lib/config/environmentalVariableValidation.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,13 @@ export const environmentalVariablesConfig: AstroUserConfig['env'] = {
4141
optional: true,
4242
}),
4343
/**
44-
* Gets the privacy policy version injected at build time via the PrivacyPolicy integration.
44+
* Gets the privacy policy version injected at build time via the PrivacyPolicyVersion integration.
45+
* No default is set intentionally — if the integration fails to inject this, getPrivacyPolicyVersion()
46+
* will throw, surfacing the configuration error rather than silently storing an invalid epoch date.
4547
*/
4648
PRIVACY_POLICY_VERSION: envField.string({
4749
access: 'public',
4850
context: 'client',
49-
default: '1970-01-01',
5051
optional: true,
5152
}),
5253
/**

0 commit comments

Comments
 (0)