Skip to content

Commit 452585e

Browse files
authored
Merge pull request #18920 from mozilla/polish-add-hsts-headers
fix(next): resolve CSP and console errors
2 parents eed4b3c + 54689fb commit 452585e

8 files changed

Lines changed: 39 additions & 32 deletions

File tree

apps/payments/next/app/[locale]/[offeringId]/[interval]/checkout/[cartId]/error/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export default async function CheckoutError({
8080
{
8181
// Once more conditionals are added, move this to a separate component
8282
cart.errorReasonId ===
83-
CartErrorReasonId.CART_ELIGIBILITY_STATUS_SAME ? (
83+
CartErrorReasonId.CART_ELIGIBILITY_STATUS_SAME ? (
8484
<Image
8585
src={checkIcon}
8686
alt="check-icon"

apps/payments/next/app/[locale]/[offeringId]/[interval]/checkout/[cartId]/start/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ export default async function Checkout({
172172
className={clsx(
173173
'font-semibold text-grey-600 text-lg mt-10 mb-5',
174174
!session?.user?.email &&
175-
'cursor-not-allowed relative focus:border-blue-400 focus:outline-none focus:shadow-input-blue-focus after:absolute after:content-[""] after:top-0 after:left-0 after:w-full after:h-full after:bg-white after:opacity-50 after:z-10 select-none'
175+
'cursor-not-allowed relative focus:border-blue-400 focus:outline-none focus:shadow-input-blue-focus after:absolute after:content-[""] after:top-0 after:left-0 after:w-full after:h-full after:bg-white after:opacity-50 after:z-10 select-none'
176176
)}
177177
data-testid="header-prefix"
178178
>
@@ -201,7 +201,7 @@ export default async function Checkout({
201201
className={clsx(
202202
'font-semibold text-grey-600 text-start',
203203
!session?.user?.email &&
204-
'cursor-not-allowed relative focus:border-blue-400 focus:outline-none focus:shadow-input-blue-focus after:absolute after:content-[""] after:top-0 after:left-0 after:w-full after:h-full after:bg-white after:opacity-50 after:z-10 select-none'
204+
'cursor-not-allowed relative focus:border-blue-400 focus:outline-none focus:shadow-input-blue-focus after:absolute after:content-[""] after:top-0 after:left-0 after:w-full after:h-full after:bg-white after:opacity-50 after:z-10 select-none'
205205
)}
206206
>
207207
{l10n.getString(

apps/payments/next/app/[locale]/[offeringId]/[interval]/location/page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ export default async function Location({
4040
const taxAddress =
4141
providedCountryCode && providedPostalCode
4242
? {
43-
countryCode: providedCountryCode,
44-
postalCode: providedPostalCode,
45-
}
43+
countryCode: providedCountryCode,
44+
postalCode: providedPostalCode,
45+
}
4646
: undefined;
4747

4848
const fxaUid = session?.user?.id;

apps/payments/next/middleware.ts

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,14 @@ export function middleware(request: NextRequest) {
2626
const PROFILE_DEFAULT_IMAGES_URL = process.env.PROFILE_DEFAULT_IMAGES_URL;
2727
const PROFILE_UPLOADED_IMAGES_URL = process.env.PROFILE_UPLOADED_IMAGES_URL;
2828

29-
/*
30-
* CSP Notes
31-
* - Next.js next/image currently causes an inline style CSP error.
32-
* There is a work around available, however at this time, we've opted
33-
* to use 'unsafe-inline' to match what's in fxa-payments-server
34-
* https://github.com/vercel/next.js/issues/45184
35-
*/
3629
const nonce = Buffer.from(crypto.randomUUID()).toString('base64');
3730
const cspHeader = `
3831
default-src 'self';
3932
connect-src 'self' https://api.stripe.com ${PAYPAL_API_URL};
40-
frame-src https://js.stripe.com https://hooks.stripe.com ${PAYPAL_API_URL} ${PAYPAL_SCRIPT_URL};
41-
script-src 'self' 'nonce-${nonce}' 'strict-dynamic' https: http: 'unsafe-inline' ${
42-
process.env.NODE_ENV === 'production' ? '' : `'unsafe-eval'`
43-
} https://js.stripe.com ${PAYPAL_SCRIPT_URL};
44-
script-src-elem 'self' 'nonce-${nonce}' 'strict-dynamic' https: http: 'unsafe-inline' ${
45-
process.env.NODE_ENV === 'production' ? '' : `'unsafe-eval'`
46-
} https://js.stripe.com;
47-
style-src 'self' 'unsafe-inline';
33+
frame-src https://*.js.stripe.com https://js.stripe.com https://hooks.stripe.com ${PAYPAL_API_URL} ${PAYPAL_SCRIPT_URL};
34+
script-src 'self' 'nonce-${nonce}' ${process.env.NODE_ENV === 'production' ? '' : `'unsafe-eval'`
35+
} https://*.js.stripe.com https://js.stripe.com ${PAYPAL_SCRIPT_URL};
36+
style-src 'self' 'unsafe-hashes' 'sha256-0hAheEzaMe6uXIKV4EehS9pu1am1lj/KnnzrOYqckXk=' 'sha256-GsQC5AaXpdCaKTyWbxBzn7nitfp0Otwn7I/zu0rUKOs=' 'sha256-zlqnbDt84zf1iSefLU/ImC54isoprH/MRiVZGskwexk=';
4837
img-src 'self' blob: data: ${accountsStaticCdn} ${PAYPAL_OBJECTS} ${PROFILE_CLIENT_URL} ${PROFILE_DEFAULT_IMAGES_URL} ${PROFILE_UPLOADED_IMAGES_URL};
4938
font-src 'self';
5039
object-src 'none';

apps/payments/next/next.config.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,19 @@ const nextConfig = {
8989
},
9090
],
9191
},
92+
async headers() {
93+
return [
94+
{
95+
source: '/(.*)',
96+
headers: [
97+
{
98+
key: 'Strict-Transport-Security',
99+
value: 'max-age=63072000; includeSubDomains; preload'
100+
}
101+
],
102+
},
103+
]
104+
},
92105
};
93106

94107
/**

libs/payments/ui/src/lib/client/components/BaseButton/index.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
* License, v. 2.0. If a copy of the MPL was not distributed with this
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

5+
import { forwardRef } from "react";
6+
57
export enum ButtonVariant {
68
Primary,
79
Secondary,
@@ -14,7 +16,7 @@ interface BaseButtonProps
1416
variant?: ButtonVariant;
1517
}
1618

17-
export function BaseButton({ children, variant, ...props }: BaseButtonProps) {
19+
export const BaseButton = forwardRef(function BaseButton({ children, variant, ...props }: BaseButtonProps, ref: React.Ref<HTMLButtonElement>) {
1820
let variantStyles = '';
1921
switch (variant) {
2022
case ButtonVariant.Primary:
@@ -33,8 +35,9 @@ export function BaseButton({ children, variant, ...props }: BaseButtonProps) {
3335
<button
3436
{...props}
3537
className={`flex items-center justify-center h-12 rounded-md p-4 z-10 cursor-pointer aria-disabled:relative aria-disabled:after:absolute aria-disabled:after:content-[''] aria-disabled:after:top-0 aria-disabled:after:left-0 aria-disabled:after:w-full aria-disabled:after:h-full aria-disabled:after:bg-white aria-disabled:after:opacity-50 aria-disabled:after:z-30 aria-disabled:border-none ${props.className} ${variantStyles}`}
38+
ref={ref}
3639
>
3740
{children}
3841
</button>
3942
);
40-
}
43+
})

libs/payments/ui/src/lib/client/components/SelectTaxLocation/index.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@ type SaveActionSignature = (
2525
postalCode: string
2626
) => Promise<
2727
| {
28-
ok: false;
29-
error: string | { message: string; data: any };
30-
}
28+
ok: false;
29+
error: string | { message: string; data: any };
30+
}
3131
| {
32-
ok: true;
33-
data: any;
34-
}
32+
ok: true;
33+
data: any;
34+
}
3535
| void
3636
>;
3737

libs/payments/ui/src/lib/client/components/SubmitButton/index.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,20 @@ import Image from 'next/image';
88
import { useFormStatus } from 'react-dom';
99
import spinnerWhiteImage from '@fxa/shared/assets/images/spinnerwhite.svg';
1010
import { BaseButton, ButtonVariant } from '../BaseButton';
11+
import { forwardRef } from 'react';
1112

1213
interface SubmitButtonProps {
1314
children: React.ReactNode;
1415
variant: ButtonVariant;
1516
}
1617

17-
export function SubmitButton({
18+
export const SubmitButton = forwardRef(function SubmitButton({
1819
children,
1920
disabled,
2021
className,
2122
variant,
2223
...otherProps
23-
}: SubmitButtonProps & React.HTMLProps<HTMLButtonElement>) {
24+
}: SubmitButtonProps & React.HTMLProps<HTMLButtonElement>, ref: React.Ref<HTMLButtonElement>) {
2425
const { pending } = useFormStatus();
2526

2627
return (
@@ -30,6 +31,7 @@ export function SubmitButton({
3031
disabled={pending || disabled}
3132
type="submit"
3233
className={className}
34+
ref={ref}
3335
>
3436
{pending ? (
3537
<>
@@ -45,4 +47,4 @@ export function SubmitButton({
4547
)}
4648
</BaseButton>
4749
);
48-
}
50+
})

0 commit comments

Comments
 (0)