Skip to content

Commit ed5c328

Browse files
committed
content security policy and brevo fixes
1 parent 9b332d0 commit ed5c328

File tree

3 files changed

+172
-97
lines changed

3 files changed

+172
-97
lines changed

next.config.ts

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,77 @@
11
import type { NextConfig } from 'next';
22

3-
const nextConfig: NextConfig = {};
3+
const self = "'self'";
4+
const unsafeInline = "'unsafe-inline'";
5+
const unsafeEval = "'unsafe-eval'";
6+
const blob = 'blob:';
7+
const data = 'data:';
8+
9+
// Supabase: allow any *.supabase.co and local dev on any port
10+
const supabaseWildcard = '*.supabase.co';
11+
const supabaseWsWildcard = 'wss://*.supabase.co';
12+
const localhostWildcard = 'localhost:*'; // covers http on any port
13+
const localhostWsWildcard = 'ws://localhost:*'; // covers ws on any port
14+
15+
// PostHog: general wildcard
16+
const posthogHosts = '*.posthog.com';
17+
18+
// Google services
19+
const googleTagManager = '*.googletagmanager.com';
20+
const googleAnalytics = '*.google-analytics.com';
21+
const googleAds = 'googleads.g.doubleclick.net';
22+
const googleAdServices = 'www.googleadservices.com';
23+
const googleFonts = 'fonts.googleapis.com';
24+
const gStatic = '*.gstatic.com'; // For fonts, images, styles from Google
25+
const googleConnect = 'www.google.com'; // For Google Ads/CCM connect
26+
27+
// Cloudflare
28+
const cfChallenges = 'challenges.cloudflare.com';
29+
30+
// Brevo (Sendinblue)
31+
const sibforms = 'sibforms.com *.sibforms.com';
32+
33+
// Stripe
34+
const stripeJs = 'js.stripe.com';
35+
const stripeFrame = '*.stripe.com'; // For frames
36+
const stripeApi = 'api.stripe.com'; // For connect
37+
38+
// Other known services
39+
const unpkg = 'unpkg.com';
40+
const vercelAssets = 'assets.vercel.com';
41+
const vercelVitals = 'vitals.vercel-insights.com';
42+
const githubAvatars = 'avatars.githubusercontent.com';
43+
const openrouter = 'openrouter.ai *.openrouter.ai';
44+
const githubCom = 'github.com'; // For form-action
45+
46+
const nextConfig: NextConfig = {
47+
async headers() {
48+
const cspHeader = [
49+
`default-src ${self}`,
50+
`script-src ${self} ${unsafeEval} ${unsafeInline} ${supabaseWildcard} ${localhostWildcard} ${cfChallenges} ${posthogHosts} ${googleTagManager} ${googleAnalytics} ${googleAds} ${googleAdServices} ${sibforms} ${stripeJs} ${unpkg} ${vercelAssets}`,
51+
`style-src ${self} ${unsafeInline} ${googleFonts} ${gStatic} ${sibforms}`,
52+
`img-src ${self} ${blob} ${data} ${supabaseWildcard} ${localhostWildcard} ${googleAnalytics} ${googleAds} ${gStatic} ${githubAvatars} ${googleConnect} ${googleAdServices}`,
53+
`font-src ${self} ${gStatic}`,
54+
`worker-src ${self} ${blob}`,
55+
`frame-src ${self} ${cfChallenges} ${sibforms} ${stripeFrame} td.doubleclick.net ${googleTagManager}`,
56+
`connect-src ${self} ${supabaseWildcard} ${supabaseWsWildcard} ${localhostWildcard} ${localhostWsWildcard} ${cfChallenges} ${posthogHosts} ${googleAnalytics} ${googleTagManager} ${googleAds} ${googleAdServices} ${googleConnect} ${sibforms} ${stripeApi} ${openrouter} ${vercelVitals}`,
57+
`object-src 'none'`, // Note: 'none' should be quoted
58+
`base-uri ${self}`,
59+
`form-action ${self} ${githubCom} ${openrouter} ${sibforms}`,
60+
`upgrade-insecure-requests`,
61+
].join('; ');
62+
63+
return [
64+
{
65+
source: '/(.*)',
66+
headers: [
67+
{
68+
key: 'Content-Security-Policy',
69+
value: cspHeader.replace(/\s{2,}/g, ' ').trim(),
70+
},
71+
],
72+
},
73+
];
74+
},
75+
};
476

577
export default nextConfig;

src/components/brevo-email-subscribe/brevo-email-subscribe.tsx

Lines changed: 98 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { useEffect } from 'react';
44
import Script from 'next/script';
55
// No specific CSS import needed if we are using Tailwind for the new design
6-
// import './brevo-email-subscribe.css';
6+
import './brevo-email-subscribe.css';
77

88
// import { Card, CardContent, CardHeader, CardTitle, CardFooter } from '@/components/ui/card'; // Card structure will be removed
99
import { Input } from '@/components/ui/input';
@@ -88,104 +88,106 @@ export const BrevoEmailSubscribe = (props: BrevoEmailSubscribeProps) => {
8888
};
8989

9090
return (
91-
<div id="brevo-email-subscribe-wrapper" className="w-full max-w-md">
92-
<div id="error-message" className="mb-4 hidden">
93-
<Alert variant="destructive">
94-
<AlertCircle className="h-4 w-4" />
95-
<AlertDescription>
96-
Your subscription could not be saved. Please try again.
97-
</AlertDescription>
98-
</Alert>
99-
</div>
100-
101-
<div id="success-message" className="mb-4 hidden">
102-
<Alert>
103-
<CheckCircle2 className="h-4 w-4" />
104-
<AlertDescription>Your subscription has been successful.</AlertDescription>
105-
</Alert>
106-
</div>
107-
108-
<form
109-
id="sib-form"
110-
method="POST"
111-
action={formSubmitUrl}
112-
data-type="subscription"
113-
onSubmit={handleSubmit}
114-
className="space-y-3"
115-
>
116-
<div className="flex flex-col sm:flex-row gap-3 w-full">
117-
<Input
118-
type="email"
119-
id="EMAIL"
120-
name="EMAIL"
121-
placeholder="Enter your email"
122-
autoComplete="off"
123-
required
124-
className="w-full bg-background border-border text-foreground placeholder:text-muted-foreground" // Adjusted styling
125-
/>
126-
<Button
127-
type="submit"
128-
size="lg"
129-
className="bg-primary hover:bg-primary/90 text-primary-foreground font-medium whitespace-nowrap sib-hide-loader-icon-parent"
130-
>
131-
Join Waitlist
132-
</Button>
91+
<div id="sib-form-container">
92+
<div id="brevo-email-subscribe-wrapper" className="w-full max-w-md">
93+
<div id="error-message" className="mb-4 hidden">
94+
<Alert variant="destructive">
95+
<AlertCircle className="h-4 w-4" />
96+
<AlertDescription>
97+
Your subscription could not be saved. Please try again.
98+
</AlertDescription>
99+
</Alert>
133100
</div>
134101

135-
<div className="flex items-center space-x-2">
136-
<Checkbox
137-
id="OPT_IN"
138-
name="OPT_IN"
139-
value="1"
140-
className="border-border data-[state=checked]:bg-primary data-[state=checked]:border-primary" // Adjusted styling
141-
/>
142-
<Label
143-
htmlFor="OPT_IN"
144-
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 text-left text-muted-foreground" // Adjusted styling
145-
>
146-
I agree to receive newsletters
147-
</Label>
102+
<div id="success-message" className="mb-4 hidden">
103+
<Alert>
104+
<CheckCircle2 className="h-4 w-4" />
105+
<AlertDescription>Your subscription has been successful.</AlertDescription>
106+
</Alert>
148107
</div>
149108

150-
<div className="text-[10px] text-muted-foreground">
151-
<p>
152-
By submitting this form you agree that the personal data you provided will be
153-
transferred to Brevo for processing in accordance with Brevo's{' '}
154-
<a
155-
href="https://www.brevo.com/en/legal/privacypolicy/"
156-
target="_blank"
157-
rel="noopener noreferrer" // Added rel for security
158-
className="underline hover:text-primary"
109+
<form
110+
id="sib-form"
111+
method="POST"
112+
action={formSubmitUrl}
113+
data-type="subscription"
114+
onSubmit={handleSubmit}
115+
className="space-y-3"
116+
>
117+
<div className="flex flex-col sm:flex-row gap-3 w-full">
118+
<Input
119+
type="email"
120+
id="EMAIL"
121+
name="EMAIL"
122+
placeholder="Enter your email"
123+
autoComplete="off"
124+
required
125+
className="w-full h-10 bg-background border-border text-foreground placeholder:text-muted-foreground" // Adjusted styling
126+
/>
127+
<Button
128+
type="submit"
129+
size="lg"
130+
className="bg-primary hover:bg-primary/90 text-primary-foreground font-medium whitespace-nowrap sib-hide-loader-icon-parent"
159131
>
160-
Privacy Policy
161-
</a>
162-
.
163-
</p>
164-
</div>
165-
166-
<div
167-
className="cf-turnstile g-recaptcha m-0" // Keep class for Brevo script
168-
data-sitekey={process.env.NEXT_PUBLIC_CF_TURNSTILE_SITE_KEY}
169-
id="sib-captcha" // Keep id for Brevo script
170-
data-callback="handleCaptchaResponse" // Keep callback for Brevo script
171-
data-language="en"
172-
/>
132+
<Loader2 className="sib-loader sib-hide-loader-icon h-4 w-4 animate-spin" />
133+
Join Waitlist
134+
</Button>
135+
</div>
136+
137+
<div className="flex items-center space-x-2">
138+
<Checkbox
139+
id="OPT_IN"
140+
name="OPT_IN"
141+
value="1"
142+
className="border-border data-[state=checked]:bg-primary data-[state=checked]:border-primary" // Adjusted styling
143+
/>
144+
<Label
145+
htmlFor="OPT_IN"
146+
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 text-left text-muted-foreground" // Adjusted styling
147+
>
148+
I agree to receive newsletters
149+
</Label>
150+
</div>
151+
152+
<div className="text-[10px] text-muted-foreground">
153+
<p>
154+
By submitting this form you agree that the personal data you provided will be
155+
transferred to Brevo for processing in accordance with Brevo's{' '}
156+
<a
157+
href="https://www.brevo.com/en/legal/privacypolicy/"
158+
target="_blank"
159+
rel="noopener noreferrer" // Added rel for security
160+
className="underline hover:text-primary"
161+
>
162+
Privacy Policy
163+
</a>
164+
.
165+
</p>
166+
</div>
167+
168+
<div
169+
className="cf-turnstile g-recaptcha m-0" // Keep class for Brevo script
170+
data-sitekey={process.env.NEXT_PUBLIC_CF_TURNSTILE_SITE_KEY}
171+
id="sib-captcha" // Keep id for Brevo script
172+
data-callback="handleCaptchaResponse" // Keep callback for Brevo script
173+
data-language="en"
174+
/>
173175

174-
<input
175-
type="text"
176-
name="email_address_check"
177-
defaultValue=""
178-
className="input--hidden m-0 h-0 block"
179-
/>
180-
<input type="hidden" name="locale" defaultValue="en" />
181-
</form>
182-
{/* Brevo's main script and Cloudflare Turnstile script */}
183-
<Script src="https://sibforms.com/forms/end-form/build/main.js" strategy="lazyOnload" />
184-
<Script src="https://challenges.cloudflare.com/turnstile/v0/api.js" strategy="lazyOnload" />
185-
<Script
186-
id="brevo-captcha-handler" // Added id for clarity
187-
dangerouslySetInnerHTML={{
188-
__html: `
176+
<input
177+
type="text"
178+
name="email_address_check"
179+
defaultValue=""
180+
className="input--hidden m-0 h-0 block"
181+
/>
182+
<input type="hidden" name="locale" defaultValue="en" />
183+
</form>
184+
{/* Brevo's main script and Cloudflare Turnstile script */}
185+
<Script src="https://sibforms.com/forms/end-form/build/main.js" strategy="lazyOnload" />
186+
<Script src="https://challenges.cloudflare.com/turnstile/v0/api.js" strategy="lazyOnload" />
187+
<Script
188+
id="brevo-captcha-handler" // Added id for clarity
189+
dangerouslySetInnerHTML={{
190+
__html: `
189191
function handleCaptchaResponse() {
190192
var event = new Event('captchaChange');
191193
document.getElementById('sib-captcha').dispatchEvent(event);
@@ -195,8 +197,9 @@ export const BrevoEmailSubscribe = (props: BrevoEmailSubscribeProps) => {
195197
}
196198
}
197199
`,
198-
}}
199-
/>
200+
}}
201+
/>
202+
</div>
200203
</div>
201204
);
202205
};

src/lib/GitHubAppService.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export class GitHubAppService extends AgentsmithSupabaseService {
103103
await this.getInstallationRepositories(installationId);
104104
} catch (error: any) {
105105
// if we get a 404 error, means the installation does not exist
106-
this.logger.error('Error fetching installation repositories:', error.status);
106+
this.logger.error('Error fetching installation repositories:', error);
107107

108108
if (error.status === 404) {
109109
return {

0 commit comments

Comments
 (0)