chore: patch deps, CSP style nonces, otplib 13, and PR CI
Deploy / release (push) Skipped
Deploy / deploy (push) Successful in 1m35s

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
SimoandCursor committed 2026-07-21 20:46:02 +02:00
1 parent 87bda8dbef
commit 2ff08e5127
11 files changed
+239 -160

No files matched your search

+38
View File
@@ -0,0 +1,38 @@
name: CI
on:
pull_request:
branches:
- main
jobs:
check:
runs-on: shell
steps:
- name: Typecheck, lint, and test
run: |
set -e
WORK="$(mktemp -d /var/tmp/epicnext-ci.XXXXXX)"
cleanup() { rm -rf "${WORK}"; }
trap cleanup EXIT
echo "--- CI checks (${WORK}) ---"
git clone --depth 50 \
/docker/gitea/gitea/git/repositories/remco/epicnext-cms.git \
"${WORK}"
cd "${WORK}"
REF="${{ gitea.sha }}"
if [ -z "${REF}" ]; then
echo "ERROR: missing gitea.sha" >&2
exit 1
fi
git fetch --depth 50 origin "${REF}"
git checkout -f "${REF}"
export SKIP_ENV_VALIDATION=1
pnpm install --frozen-lockfile
pnpm prisma:generate
pnpm biome:lint
pnpm typecheck
pnpm test
echo "--- CI checks passed ---"
+3 -2
View File
@@ -1,12 +1,13 @@
{
"$schema": "https://biomejs.dev/schemas/2.5.4/schema.json",
"$schema": "https://biomejs.dev/schemas/2.5.5/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
},
"files": {
"ignoreUnknown": false
"ignoreUnknown": false,
"includes": ["**", "!setup", "!public"]
},
"formatter": {
"enabled": true,
+7 -7
View File
@@ -31,7 +31,7 @@
"@prisma/adapter-mariadb": "^7.9.0",
"@prisma/client": "^7.9.0",
"@sentry/nextjs": "^10.67.0",
"@tanstack/react-virtual": "^3.14.6",
"@tanstack/react-virtual": "^3.14.7",
"bcrypt": "^6.0.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
@@ -47,18 +47,18 @@
"lucide-react": "^1.25.0",
"lzma": "^2.3.2",
"music-metadata": "^11.14.0",
"mysql2": "^3.23.0",
"mysql2": "^3.23.1",
"next": "^16.2.11",
"next-auth": "5.0.0-beta.32",
"next-intl": "^4.13.3",
"next-view-transitions": "^0.3.5",
"nodemailer": "^7.0.13",
"otplib": "^12.0.1",
"otplib": "^13.4.1",
"pino": "^10.3.1",
"react": "^19.2.8",
"react-dom": "^19.2.8",
"react-hook-form": "^7.81.0",
"resend": "^6.17.2",
"react-hook-form": "^7.82.0",
"resend": "^6.18.0",
"sanitize-html": "^2.17.6",
"server-only": "^0.0.1",
"sharp": "^0.35.3",
@@ -68,7 +68,7 @@
"zod": "^4.4.3"
},
"devDependencies": {
"@biomejs/biome": "2.5.4",
"@biomejs/biome": "2.5.5",
"@next/bundle-analyzer": "^16.2.11",
"@tailwindcss/forms": "^0.5.11",
"@tailwindcss/postcss": "^4.3.3",
@@ -83,7 +83,7 @@
"dotenv": "^16.4.0",
"knip": "^6.27.0",
"pino-pretty": "^13.1.3",
"postcss": "^8.5.19",
"postcss": "^8.5.21",
"prisma": "^7.9.0",
"tailwindcss": "^4.3.3",
"tsx": "^4.23.1",
+142 -130
View File
@@ -27,7 +27,7 @@ importers:
version: 3.2.2([email protected])
'@hookform/resolvers':
specifier: ^5.4.0
version: 5.4.0([email protected]1.0([email protected]))
version: 5.4.0([email protected]2.0([email protected]))
'@prisma/adapter-mariadb':
specifier: ^7.9.0
version: 7.9.0
@@ -36,10 +36,10 @@ importers:
version: 7.9.0([email protected](@types/[email protected](@types/[email protected]))(@types/[email protected])([email protected]([email protected]))([email protected])([email protected]))([email protected])
'@sentry/nextjs':
specifier: ^10.67.0
version: 10.67.0(@opentelemetry/[email protected](@opentelemetry/[email protected]))(@opentelemetry/[email protected](@opentelemetry/[email protected]))([email protected](@babel/[email protected])(@opentelemetry/[email protected])([email protected])([email protected]([email protected]))([email protected]))([email protected])([email protected]([email protected])([email protected]9))
version: 10.67.0(@opentelemetry/[email protected](@opentelemetry/[email protected]))(@opentelemetry/[email protected](@opentelemetry/[email protected]))([email protected](@babel/[email protected])(@opentelemetry/[email protected])([email protected])([email protected]([email protected]))([email protected]))([email protected])([email protected]([email protected])([email protected].21))
'@tanstack/react-virtual':
specifier: ^3.14.6
version: 3.14.6([email protected]([email protected]))([email protected])
specifier: ^3.14.7
version: 3.14.7([email protected]([email protected]))([email protected])
bcrypt:
specifier: ^6.0.0
version: 6.0.0
@@ -86,8 +86,8 @@ importers:
specifier: ^11.14.0
version: 11.14.0
mysql2:
specifier: ^3.23.0
version: 3.23.0(@types/[email protected])
specifier: ^3.23.1
version: 3.23.1(@types/[email protected])
next:
specifier: ^16.2.11
version: 16.2.11(@babel/[email protected])(@opentelemetry/[email protected])([email protected])([email protected]([email protected]))([email protected])
@@ -104,8 +104,8 @@ importers:
specifier: ^7.0.13
version: 7.0.13
otplib:
specifier: ^12.0.1
version: 12.0.1
specifier: ^13.4.1
version: 13.4.1
pino:
specifier: ^10.3.1
version: 10.3.1
@@ -116,11 +116,11 @@ importers:
specifier: ^19.2.8
version: 19.2.8([email protected])
react-hook-form:
specifier: ^7.81.0
version: 7.81.0([email protected])
specifier: ^7.82.0
version: 7.82.0([email protected])
resend:
specifier: ^6.17.2
version: 6.17.2
specifier: ^6.18.0
version: 6.18.0
sanitize-html:
specifier: ^2.17.6
version: 2.17.6
@@ -144,8 +144,8 @@ importers:
version: 4.4.3
devDependencies:
'@biomejs/biome':
specifier: 2.5.4
version: 2.5.4
specifier: 2.5.5
version: 2.5.5
'@next/bundle-analyzer':
specifier: ^16.2.11
version: 16.2.11
@@ -190,7 +190,7 @@ importers:
version: 13.1.3
postcss:
specifier: ^8.5.19
version: 8.5.19
version: 8.5.21
prisma:
specifier: ^7.9.0
version: 7.9.0(@types/[email protected](@types/[email protected]))(@types/[email protected])([email protected]([email protected]))([email protected])([email protected])
@@ -339,59 +339,59 @@ packages:
'@types/react':
optional: true
'@biomejs/[email protected].4':
resolution: {integrity: sha512-xy5FNE5kQJKyK5MR1gJy6ztXYx4WBAbYGlK04lMEgmyPRWKybY9NFwiG9yo0XdzOU8Xvhj41u034J1ywfoWfMw==}
'@biomejs/[email protected].5':
resolution: {integrity: sha512-r1S8nFsAG1MY+vJFZALzIvwXAJv6ejDQ0mxP21Tgr9YK3ZFtjrvbBwDdNhx1rUqvccEIeNg20cYCNzl6Cr69pQ==}
engines: {node: '>=14.21.3'}
hasBin: true
'@biomejs/[email protected].4':
resolution: {integrity: sha512-4o3NFRobXHynkgcFVrlZsoDAFtF2ldlEGN8sORSws5ZQqyY4PXnPUIylu4ksfyHuwkfvDREuWh3JK+niRwGq3w==}
'@biomejs/[email protected].5':
resolution: {integrity: sha512-kUrAhXVWUrwmAUnV2iXSK7umxKFysTwvqK+Ty6ptUcLY/7T3SnCAjUowE4uvwaEej6nXZ7hu/dTtbokKdsPeag==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [darwin]
'@biomejs/[email protected].4':
resolution: {integrity: sha512-D32P5HkU2Y6PySuC/WsVDTOgsDwVFmujzhhhOQjajtATpVWFDXuVd3oRbsWNSEA+aaFzyzZm22szsyydBYlSyQ==}
'@biomejs/[email protected].5':
resolution: {integrity: sha512-DamiYc5bUYZ2uxlfc+RLEPtz1Abb6PO5eTbOkufLpSGwd/7AMQAdxhFYiXmwwkJL8IsT8S7GvdgwDHqaMFAvKw==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [darwin]
'@biomejs/[email protected].4':
resolution: {integrity: sha512-Rpm5/AT1m+DlJmUoYvS4/vXc+0tXJPJ2NQz25TGPyHVF5JrWy75PE0GH6kVxsKtQDuCH4OgzquZq0R4kj/wCVg==}
'@biomejs/[email protected].5':
resolution: {integrity: sha512-U4WMl/sy/E/Q73vf15VspakLRRs2LDFcCeBxJnQfXzssb88zpV6PJPaQ3ezhQ7H6Ht2/8bvuZeHgJWzmoxllZg==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@biomejs/[email protected].4':
resolution: {integrity: sha512-pSEfW7B8kTsXUjUxC1xVVK+y85Ht3C5XxZ9gclmC7/3Ku9Vqz8jmI7k0p/BNIjQ6t4sFERI2sFeH73ybiZl6YQ==}
'@biomejs/[email protected].5':
resolution: {integrity: sha512-lRKF/pH/1RiYiBKExi3TCZVAtvzEm77aifrvcNiDFrR9WxeAnDUjDnseb6y2XV85mjitLs6SILGm2XG77cHtSQ==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@biomejs/[email protected].4':
resolution: {integrity: sha512-aby/PohmmgbShcHqFsZVzG8H6D98+P+A6xRWRrQcLW1pCjabcov5UUlke4UqNQBYTkDQav+jB4zyyDDeKB2GaA==}
'@biomejs/[email protected].5':
resolution: {integrity: sha512-m7wC7tjX5Lrmo69dc4md8FeKpPU1NTCY1v7xUoQQ2vadWwNnBS0KZOG8471otFPHrTHihQJAjQPgMObpLvDe6A==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [linux]
libc: [musl]
'@biomejs/[email protected].4':
resolution: {integrity: sha512-FNxojWJkL7EajAuzBgoLe0T2G0y112M4lBrDIFl/DomFTx8yqenYOIdsRLNXvOvBBofE8hJi85LjzLmBDpY7/Q==}
'@biomejs/[email protected].5':
resolution: {integrity: sha512-H/O39nJEw/2Zm/fm7hrmxxoF8kK/aU1uCoPp70ruXVbomaAdLpJJnCmL11Q2JotT8QVHH06So04Oq53lCSwSwQ==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@biomejs/[email protected].4':
resolution: {integrity: sha512-emoXexPZIPAZkz2RKmA95WJUqK3I5MJNYtwEbL5ESciRzhmFMMyekDhNG8hpeOaK+ZGRDxAU4wvGuA5IHQ0h0w==}
'@biomejs/[email protected].5':
resolution: {integrity: sha512-7BryINPuYypLUAH3o/o5ZdgomJ4zn3EDR0ChZJst7n32S6ZhKbgHXuYydLu+YAnx59ehGFR0z/MG6qnzQi3Yyw==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [win32]
'@biomejs/[email protected].4':
resolution: {integrity: sha512-U1jaluLw1qQc2Tx7/CeSoL9N5XcqIH+GWjpUAy1ouB5nVjSCMNO+NNHdY3RAs8zxNurLWAdj6pehQdCA2zyU+Q==}
'@biomejs/[email protected].5':
resolution: {integrity: sha512-bIBFo+n6MIxdNcVFy5CrurbKiZQiUciK3bt8+O9I4wjFZNTfXLpi+giq47522eXqW5NBc9ulx7dR1SlZKi2J5g==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [win32]
@@ -1039,6 +1039,10 @@ packages:
cpu: [x64]
os: [win32]
'@noble/[email protected]':
resolution: {integrity: sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==}
engines: {node: '>= 20.19.0'}
'@opentelemetry/[email protected]':
resolution: {integrity: sha512-CmVa4ImJ+ynfrPMNaAXHET6Bhb44SwzmfyVJFq9ni2jgXJR/l7C6gfVFddNmHP+ZOkP9cf4f9DBe68qVLTHc9w==}
engines: {node: '>=8.0.0'}
@@ -1081,23 +1085,23 @@ packages:
resolution: {integrity: sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==}
engines: {node: '>=14'}
'@otplib/core@12.0.1':
resolution: {integrity: sha512-4sGntwbA/AC+SbPhbsziRiD+jNDdIzsZ3JUyfZwjtKyc/wufl1pnSIaG4Uqx8ymPagujub0o92kgBnB89cuAMA==}
'@otplib/core@13.4.1':
resolution: {integrity: sha512-KIXgK1hNtWJEBMTastbe1bpmuais+3f+ATeO8TkMs2rNkfGO1FbQy8+/UWVEu3TR/iTJerU0idkPudaPmLP2BA==}
'@otplib/[email protected].1':
resolution: {integrity: sha512-qPuhN3QrT7ZZLcLCyKOSNhuijUi9G5guMRVrxq63r9YNOxxQjPm59gVxLM+7xGnHnM6cimY57tuKsjK7y9LM1g==}
deprecated: Please upgrade to v13 of otplib. Refer to otplib docs for migration paths
'@otplib/[email protected].1':
resolution: {integrity: sha512-g9q04SwpG5ZtMnVkUcgcoAlwCH4YLROZN1qhyBwgkBzqYYVSYhpP6gSGaxGHwePLt1c+e6NqDlgIZN+e1/XPuA==}
'@otplib/plugin-[email protected].1':
resolution: {integrity: sha512-MtT+uqRso909UkbrrYpJ6XFjj9D+x2Py7KjTO9JDPhL0bJUYVu5kFP4TFZW4NFAywrAtFRxOVY261u0qwb93gA==}
deprecated: Please upgrade to v13 of otplib. Refer to otplib docs for migration paths
'@otplib/plugin-[email protected].1':
resolution: {integrity: sha512-Fs/r5qisC05SRhT6xWXaypB6PVC0vgWf6zztmi0J5RnQ09OJiPDWCJFH6cDm6ANsrdvB9di7X+Jb7L13BoEbUA==}
'@otplib/p[email protected].1':
resolution: {integrity: sha512-xf1v9oOJRyXfluBhMdpOkr+bsE+Irt+0D5uHtvg6x1eosfmHCsCC6ej/m7FXiWqdo0+ZUI6xSKDhJwc8yfiOPQ==}
deprecated: Please upgrade to v13 of otplib. Refer to otplib docs for migration paths
'@otplib/p[email protected].1':
resolution: {integrity: sha512-PJfVW8/1hdS6CfxLheKPZSLTwDq4TijZbN4yRjxlv0ODdzmxpM+wGwWr1JXMdy0xJPxLziydQD5gdVqrR4/gAg==}
'@otplib/[email protected].1':
resolution: {integrity: sha512-9hSetMI7ECqbFiKICrNa4w70deTUfArtwXykPUvSHWOdzOlfa9ajglu7mNCntlvxycTiOAXkQGwjQCzzDEMRMg==}
'@otplib/[email protected].1':
resolution: {integrity: sha512-QOkBVPrf6AM4qZaReZPSk9/I8ATVdZpIISJz115MqeVtcrbcr5llPZ0J7804tpnjnp1vCRkI5Qjd47HhgVteBQ==}
'@otplib/[email protected]':
resolution: {integrity: sha512-xaIm7bvICMhoB2rZIR5luiaMdssWR5nY5nXnR1fdezUgZuEO58D6zrGzLp7pQuBmlpmL0HagnscDQFoskp9yiA==}
'@oxc-parser/[email protected]':
resolution: {integrity: sha512-KDs+0VPdEmasOkpuJHW9V5WCF+cvYdMQv2Jd+aJXt+cxIx12NToRQRbXaRwUEDsZw+/jMk81Ve8ZFbjUkJTOwA==}
@@ -1988,6 +1992,9 @@ packages:
'@schummar/[email protected]':
resolution: {integrity: sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==}
'@scure/[email protected]':
resolution: {integrity: sha512-b8XEupJibegiXV+tDUseI8oLQc8ei3d/4Jkb2RpbHh3MfE054ov3uIz2dhFkB3FI8iwYkEh0gGCApkrYggkPNg==}
'@sentry/[email protected]':
resolution: {integrity: sha512-p4q8gn8wcFqZGP/s2MnJCAAd8fTikaU6A0mM97RDHQgStcrYiaS0Sc5zUNfb1V+UOLPuvdEdL6MwyxfzjYJQTA==}
engines: {node: '>= 18'}
@@ -2358,14 +2365,14 @@ packages:
peerDependencies:
tailwindcss: '>=3.0.0 || >=4.0.0 || insiders'
'@tanstack/[email protected].6':
resolution: {integrity: sha512-4+Uq8m0/gzO4kMCHUEpTtGX1RnONK0C+g88b2ltwPMWUBiaVarBuWKoPJaz7gj1cKCVRAdyu+U8GcKhwCc2beA==}
'@tanstack/[email protected].7':
resolution: {integrity: sha512-11uSrj77IDijNBqizD4lY4y1laMyRrqMLSxjnWy5CvWkCjyRDW+gGmxYq0lwQKVas/sq7zyzYWXbL/BvBzR32g==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
'@tanstack/[email protected].4':
resolution: {integrity: sha512-nGm5KteqxasUdThLc2izl6dHUqLv0LQj7Nuyo5gYalTPf/U8a9ermvsl7reT+6ioBW1l8WfpP/mcU338nLXpqw==}
'@tanstack/[email protected].5':
resolution: {integrity: sha512-AXfBC3sq6PuYSwyxYORqqgHCNjPGAvKJvZuBBJ1klhztWBB5cgqgwsq8+fNfaQJG7/K4xYBja9S90QFn2zmQAg==}
'@tokenizer/[email protected]':
resolution: {integrity: sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==}
@@ -3444,8 +3451,8 @@ packages:
resolution: {integrity: sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==}
engines: {node: '>= 8.0'}
[email protected].0:
resolution: {integrity: sha512-ZwyGLoG9BRlL7hKNDcIy7q18hA6WTWGEpLAerGHXj5Xahemia4msZdwjIOUIzesjK3B5z5gWLktf+2tYSLAyTA==}
[email protected].1:
resolution: {integrity: sha512-tTuRnC7qCet2IOfSNMYZ5SwXuBnfvBPAcIA28P0gtruXyZlU1LMxA6uha32kYypoFgyYklMqhLWwt4laYwXR/Q==}
engines: {node: '>= 8.0'}
peerDependencies:
'@types/node': '>= 8'
@@ -3572,8 +3579,8 @@ packages:
resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==}
hasBin: true
otplib@12.0.1:
resolution: {integrity: sha512-xDGvUOQjop7RDgxTQ+o4pOol0/3xSZzawTiPKRrHnQWAy0WjhNs/5HdIDJCrqC4MBynmjXgULc6YfioaxZeFgg==}
otplib@13.4.1:
resolution: {integrity: sha512-o5CxfDw6bh7hoDv0NUUIcc0RqzJ9ipfUrzeKheKJ+vs4rXZnDlA9n4a/7R1cDjpmLjKLix4BgNVRmoDkm5rLSQ==}
[email protected]:
resolution: {integrity: sha512-yFImD+WLElJpLKy8llG1qe4DCmMsL18peRp8XP1JKfig/gISbJkglnpDtX2aTmAn10kZF7164HbN2H8QPsXxGg==}
@@ -3641,15 +3648,15 @@ packages:
[email protected]:
resolution: {integrity: sha512-ECF4zHLbUItpUgE3OTtLKlPjeBN+fKEczj2zYjDfCGOzicNs0GK3Vg2IoAYwx7LH/XYw43fZQP6xnZ4TkNxSLQ==}
[email protected].4:
resolution: {integrity: sha512-0WdnFQYUrPGGTFu1uOqD2s7omwua8xaeYGdO6rb88oD5yJ/4pPHDA4sdWqfD8wQVfCny563n/HQS7zTFft+f/g==}
[email protected].5:
resolution: {integrity: sha512-GNEXKvWFQnbgO5NlrGzVa0FmWzBZ24PersAWErttSg1Hjpf0ATxTwS5DOMGaOpTG6bUh5cTr7xi0jAD942wCJA==}
[email protected]:
resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==}
engines: {node: '>=4'}
[email protected]9:
resolution: {integrity: sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==}
[email protected].21:
resolution: {integrity: sha512-v4sDNP3fdNiWMfabO7OwOQdOX8TiQSztKyT1Wj0w+j7LDallJThJRBBBmzVGyYj0crMh7jlV4zepPkiNu9UwDQ==}
engines: {node: ^10 || ^12 || >=14}
[email protected]:
@@ -3710,8 +3717,8 @@ packages:
peerDependencies:
react: ^19.2.8
[email protected]1.0:
resolution: {integrity: sha512-ocbmr2p5KBMoAfj4WCUvped33lVi1Kd5DuDUvQDnB6VEAacOjPI/jMbtDdbhco4y9ct4xUuCmMY0b/C9L0QHjw==}
[email protected]2.0:
resolution: {integrity: sha512-Zw/uFZ2dO+02GHlBn7JFGn8kZJ7LdM33B/0BXOovzFay+CMhf94JMw5BVu+F1tVkUKjNvBuaE3fz5BJhga10Tg==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: ^16.8.0 || ^17 || ^18 || ^19
@@ -3786,8 +3793,8 @@ packages:
[email protected]:
resolution: {integrity: sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==}
[email protected]7.2:
resolution: {integrity: sha512-hbaXEORFIFfT2Bh03NsA/akTTTKkD1hiuJ88ke64c5dWVV6DyoLxzFve3OiZqVOQ+JKLJ5uVkPR6hlUslpJFoA==}
[email protected]8.0:
resolution: {integrity: sha512-EjxZ9AVzywJgOlUoIJe9ytBWVrfbUtJbjeoLnRSvpU1sv97Hh9DSwhw+k8kiujrG4Rg4bzTBsjlmwWWuoOxSug==}
engines: {node: '>=20'}
peerDependencies:
'@react-email/render': '*'
@@ -4006,10 +4013,6 @@ packages:
engines: {node: '>=10'}
hasBin: true
[email protected]:
resolution: {integrity: sha512-OEI0IWCe+Dw46019YLl6V10Us5bi574EvlJEOcAkB29IzQ/mYD1A6RyNHLjZPiHCmuodxvgF6U+vZO1L15lxVA==}
engines: {node: '>=0.2.6'}
[email protected]:
resolution: {integrity: sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==}
engines: {node: '>=20'}
@@ -4447,39 +4450,39 @@ snapshots:
optionalDependencies:
'@types/react': 19.2.17
'@biomejs/[email protected].4':
'@biomejs/[email protected].5':
optionalDependencies:
'@biomejs/cli-darwin-arm64': 2.5.4
'@biomejs/cli-darwin-x64': 2.5.4
'@biomejs/cli-linux-arm64': 2.5.4
'@biomejs/cli-linux-arm64-musl': 2.5.4
'@biomejs/cli-linux-x64': 2.5.4
'@biomejs/cli-linux-x64-musl': 2.5.4
'@biomejs/cli-win32-arm64': 2.5.4
'@biomejs/cli-win32-x64': 2.5.4
'@biomejs/cli-darwin-arm64': 2.5.5
'@biomejs/cli-darwin-x64': 2.5.5
'@biomejs/cli-linux-arm64': 2.5.5
'@biomejs/cli-linux-arm64-musl': 2.5.5
'@biomejs/cli-linux-x64': 2.5.5
'@biomejs/cli-linux-x64-musl': 2.5.5
'@biomejs/cli-win32-arm64': 2.5.5
'@biomejs/cli-win32-x64': 2.5.5
'@biomejs/[email protected].4':
'@biomejs/[email protected].5':
optional: true
'@biomejs/[email protected].4':
'@biomejs/[email protected].5':
optional: true
'@biomejs/[email protected].4':
'@biomejs/[email protected].5':
optional: true
'@biomejs/[email protected].4':
'@biomejs/[email protected].5':
optional: true
'@biomejs/[email protected].4':
'@biomejs/[email protected].5':
optional: true
'@biomejs/[email protected].4':
'@biomejs/[email protected].5':
optional: true
'@biomejs/[email protected].4':
'@biomejs/[email protected].5':
optional: true
'@biomejs/[email protected].4':
'@biomejs/[email protected].5':
optional: true
'@borewit/[email protected]': {}
@@ -4655,10 +4658,10 @@ snapshots:
dependencies:
'@formatjs/fast-memoize': 3.1.7
'@hookform/[email protected]([email protected]1.0([email protected]))':
'@hookform/[email protected]([email protected]2.0([email protected]))':
dependencies:
'@standard-schema/utils': 0.3.0
react-hook-form: 7.81.0([email protected])
react-hook-form: 7.82.0([email protected])
'@img/[email protected]': {}
@@ -4933,6 +4936,8 @@ snapshots:
'@next/[email protected]':
optional: true
'@noble/[email protected]': {}
'@opentelemetry/[email protected]':
dependencies:
'@opentelemetry/api': 1.9.1
@@ -4976,28 +4981,32 @@ snapshots:
'@opentelemetry/[email protected]': {}
'@otplib/core@12.0.1': {}
'@otplib/core@13.4.1': {}
'@otplib/[email protected].1':
'@otplib/[email protected].1':
dependencies:
'@otplib/core': 12.0.1
'@otplib/core': 13.4.1
'@otplib/uri': 13.4.1
'@otplib/plugin-[email protected].1':
'@otplib/plugin-[email protected].1':
dependencies:
'@otplib/core': 12.0.1
thirty-two: 1.0.2
'@otplib/core': 13.4.1
'@scure/base': 2.2.0
'@otplib/p[email protected].1':
'@otplib/p[email protected].1':
dependencies:
'@otplib/core': 12.0.1
'@otplib/plugin-crypto': 12.0.1
'@otplib/plugin-thirty-two': 12.0.1
'@noble/hashes': 2.2.0
'@otplib/core': 13.4.1
'@otplib/[email protected].1':
'@otplib/[email protected].1':
dependencies:
'@otplib/core': 12.0.1
'@otplib/plugin-crypto': 12.0.1
'@otplib/plugin-thirty-two': 12.0.1
'@otplib/core': 13.4.1
'@otplib/hotp': 13.4.1
'@otplib/uri': 13.4.1
'@otplib/[email protected]':
dependencies:
'@otplib/core': 13.4.1
'@oxc-parser/[email protected]':
optional: true
@@ -5635,6 +5644,8 @@ snapshots:
'@schummar/[email protected]': {}
'@scure/[email protected]': {}
'@sentry/[email protected]': {}
'@sentry/[email protected]':
@@ -5664,7 +5675,7 @@ snapshots:
- encoding
- supports-color
'@sentry/[email protected]([email protected])([email protected]([email protected])([email protected]9))':
'@sentry/[email protected]([email protected])([email protected]([email protected])([email protected].21))':
dependencies:
'@babel/core': 7.29.7
'@sentry/cli': 2.58.6
@@ -5675,7 +5686,7 @@ snapshots:
magic-string: 0.30.21
optionalDependencies:
rollup: 4.62.2
webpack: 5.108.4([email protected])([email protected]9)
webpack: 5.108.4([email protected])([email protected].21)
transitivePeerDependencies:
- encoding
- supports-color
@@ -5738,7 +5749,7 @@ snapshots:
dependencies:
'@sentry/core': 10.67.0
'@sentry/[email protected](@opentelemetry/[email protected](@opentelemetry/[email protected]))(@opentelemetry/[email protected](@opentelemetry/[email protected]))([email protected](@babel/[email protected])(@opentelemetry/[email protected])([email protected])([email protected]([email protected]))([email protected]))([email protected])([email protected]([email protected])([email protected]9))':
'@sentry/[email protected](@opentelemetry/[email protected](@opentelemetry/[email protected]))(@opentelemetry/[email protected](@opentelemetry/[email protected]))([email protected](@babel/[email protected])(@opentelemetry/[email protected])([email protected])([email protected]([email protected]))([email protected]))([email protected])([email protected]([email protected])([email protected].21))':
dependencies:
'@opentelemetry/api': 1.9.1
'@rollup/plugin-commonjs': 28.0.1([email protected])
@@ -5751,7 +5762,7 @@ snapshots:
'@sentry/react': 10.67.0([email protected])
'@sentry/server-utils': 10.67.0
'@sentry/vercel-edge': 10.67.0
'@sentry/webpack-plugin': 5.4.0([email protected])([email protected]([email protected])([email protected]9))
'@sentry/webpack-plugin': 5.4.0([email protected])([email protected]([email protected])([email protected].21))
next: 16.2.11(@babel/[email protected])(@opentelemetry/[email protected])([email protected])([email protected]([email protected]))([email protected])
rollup: 4.62.2
stacktrace-parser: 0.1.11
@@ -5831,10 +5842,10 @@ snapshots:
'@opentelemetry/api': 1.9.1
'@sentry/core': 10.67.0
'@sentry/[email protected]([email protected])([email protected]([email protected])([email protected]9))':
'@sentry/[email protected]([email protected])([email protected]([email protected])([email protected].21))':
dependencies:
'@sentry/bundler-plugins': 10.66.0([email protected])([email protected]([email protected])([email protected]9))
webpack: 5.108.4([email protected])([email protected]9)
'@sentry/bundler-plugins': 10.66.0([email protected])([email protected]([email protected])([email protected].21))
webpack: 5.108.4([email protected])([email protected].21)
transitivePeerDependencies:
- encoding
- rollup
@@ -5981,7 +5992,7 @@ snapshots:
'@alloc/quick-lru': 5.2.0
'@tailwindcss/node': 4.3.3
'@tailwindcss/oxide': 4.3.3
postcss: 8.5.19
postcss: 8.5.21
tailwindcss: 4.3.3
'@tailwindcss/[email protected]([email protected])':
@@ -5989,13 +6000,13 @@ snapshots:
postcss-selector-parser: 6.0.10
tailwindcss: 4.3.3
'@tanstack/[email protected].6([email protected]([email protected]))([email protected])':
'@tanstack/[email protected].7([email protected]([email protected]))([email protected])':
dependencies:
'@tanstack/virtual-core': 3.17.4
'@tanstack/virtual-core': 3.17.5
react: 19.2.8
react-dom: 19.2.8([email protected])
'@tanstack/[email protected].4': {}
'@tanstack/[email protected].5': {}
'@tokenizer/[email protected]':
dependencies:
@@ -6997,16 +7008,16 @@ snapshots:
[email protected]: {}
[email protected]([email protected])([email protected]9)([email protected]([email protected])([email protected]9)):
[email protected]([email protected])([email protected].21)([email protected]([email protected])([email protected].21)):
dependencies:
'@jridgewell/trace-mapping': 0.3.31
jest-worker: 27.5.1
schema-utils: 4.3.3
terser: 5.49.0
webpack: 5.108.4([email protected])([email protected]9)
webpack: 5.108.4([email protected])([email protected].21)
optionalDependencies:
esbuild: 0.28.1
postcss: 8.5.19
postcss: 8.5.21
[email protected]: {}
@@ -7049,7 +7060,7 @@ snapshots:
seq-queue: 0.0.5
sqlstring: 2.3.3
[email protected].0(@types/[email protected]):
[email protected].1(@types/[email protected]):
dependencies:
'@types/node': 26.1.1
aws-ssl-profiles: 1.1.2
@@ -7110,7 +7121,7 @@ snapshots:
'@swc/helpers': 0.5.15
baseline-browser-mapping: 2.10.43
caniuse-lite: 1.0.30001806
postcss: 8.5.19
postcss: 8.5.21
react: 19.2.8
react-dom: 19.2.8([email protected])
styled-jsx: 5.1.6(@babel/[email protected])([email protected])
@@ -7158,11 +7169,14 @@ snapshots:
[email protected]: {}
otplib@12.0.1:
otplib@13.4.1:
dependencies:
'@otplib/core': 12.0.1
'@otplib/preset-default': 12.0.1
'@otplib/preset-v11': 12.0.1
'@otplib/core': 13.4.1
'@otplib/hotp': 13.4.1
'@otplib/plugin-base32-scure': 13.4.1
'@otplib/plugin-crypto-noble': 13.4.1
'@otplib/totp': 13.4.1
'@otplib/uri': 13.4.1
[email protected]:
dependencies:
@@ -7284,14 +7298,14 @@ snapshots:
[email protected]: {}
[email protected].4: {}
[email protected].5: {}
[email protected]:
dependencies:
cssesc: 3.0.0
util-deprecate: 1.0.2
[email protected]9:
[email protected].21:
dependencies:
nanoid: 3.3.16
picocolors: 1.1.1
@@ -7355,7 +7369,7 @@ snapshots:
react: 19.2.8
scheduler: 0.27.0
[email protected]1.0([email protected]):
[email protected]2.0([email protected]):
dependencies:
react: 19.2.8
@@ -7423,9 +7437,9 @@ snapshots:
[email protected]: {}
[email protected]7.2:
[email protected]8.0:
dependencies:
postal-mime: 2.7.4
postal-mime: 2.7.5
standardwebhooks: 1.0.0
[email protected]: {}
@@ -7506,7 +7520,7 @@ snapshots:
is-plain-object: 5.0.0
launder: 1.7.1
parse-srcset: 1.0.2
postcss: 8.5.19
postcss: 8.5.21
[email protected]: {}
@@ -7695,8 +7709,6 @@ snapshots:
commander: 2.20.3
source-map-support: 0.5.21
[email protected]: {}
[email protected]:
dependencies:
real-require: 1.0.0
@@ -7787,7 +7799,7 @@ snapshots:
dependencies:
lightningcss: 1.32.0
picomatch: 4.0.5
postcss: 8.5.19
postcss: 8.5.21
rolldown: 1.1.5
tinyglobby: 0.2.17
optionalDependencies:
@@ -7856,7 +7868,7 @@ snapshots:
[email protected]: {}
[email protected]([email protected])([email protected]9):
[email protected]([email protected])([email protected].21):
dependencies:
'@types/estree': 1.0.9
'@types/json-schema': 7.0.15
@@ -7874,7 +7886,7 @@ snapshots:
graceful-fs: 4.2.11
loader-runner: 4.3.2
mime-db: 1.54.0
minimizer-webpack-plugin: 5.6.1([email protected])([email protected]9)([email protected]([email protected])([email protected]9))
minimizer-webpack-plugin: 5.6.1([email protected])([email protected].21)([email protected]([email protected])([email protected].21))
neo-async: 2.6.2
schema-utils: 4.3.3
tapable: 2.3.3
@@ -1,5 +1,4 @@
import { withAdmin } from "@/lib/api-handler";
import { apiError } from "@/lib/api-response";
import { PERMS } from "@/lib/permissions";
import { prisma } from "@/lib/prisma";
import { logAudit } from "@/lib/services/audit";
+1 -1
View File
@@ -87,7 +87,7 @@ export default async function RootLayout({
style={{ backgroundColor: "var(--color-background)" }}
>
<NextIntlClientProvider locale={locale} messages={messages}>
<ThemeVars />
<ThemeVars nonce={nonce} />
<ViewTransitions>
{children}
</ViewTransitions>
+6 -3
View File
@@ -9,7 +9,7 @@ import {
// Injects the DB-driven CSS custom properties into :root / html.dark.
// Readable (WCAG) text colors are derived centrally in themePaletteCss.
export async function ThemeVars() {
export async function ThemeVars({ nonce }: { nonce?: string } = {}) {
const g = (k: string, d: string) => siteSettings.get(k, d);
const [
primary,
@@ -175,10 +175,13 @@ export async function ThemeVars() {
return (
<>
{googleHref ? <link rel="stylesheet" href={googleHref} /> : null}
<style dangerouslySetInnerHTML={{ __html: css }} />
<style nonce={nonce} dangerouslySetInnerHTML={{ __html: css }} />
{customCss?.trim() ? (
// Staff-authored custom CSS (housekeeping → Theme). Trusted input.
<style dangerouslySetInnerHTML={{ __html: customCss }} />
<style
nonce={nonce}
dangerouslySetInnerHTML={{ __html: customCss }}
/>
) : null}
</>
);
+28 -8
View File
@@ -1,13 +1,29 @@
import { authenticator } from "otplib";
import {
createGuardrails,
generateSecret,
generateSync,
generateURI,
verifySync,
} from "otplib";
// Laravel Fortify uses pragmarx/google2fa: HMAC-SHA1, 6 digits, 30s period.
// otplib already defaults to SHA1/6/30; window=1 tolerates one step of skew.
authenticator.options = { window: 1 };
// Laravel Fortify / pragmarx google2fa: HMAC-SHA1, 6 digits, 30s period.
// Allow legacy secrets shorter than otplib v13's default 16-byte minimum
// (many existing AtomCMS/Fortify secrets decode to ~10 bytes).
const guardrails = createGuardrails({ MIN_SECRET_BYTES: 10 });
// ±1 time-step of clock skew (30s period → epochTolerance 30).
const EPOCH_TOLERANCE = 30;
/** Verify a 6-digit TOTP code against a base32 secret. */
export function verifyTotp(token: string, secret: string): boolean {
try {
return authenticator.check(token, secret);
const result = verifySync({
secret,
token,
epochTolerance: EPOCH_TOLERANCE,
guardrails,
});
return result.valid === true;
} catch {
return false;
}
@@ -15,12 +31,12 @@ export function verifyTotp(token: string, secret: string): boolean {
/** Current TOTP code for a secret (used in tests / tooling). */
export function generateTotp(secret: string): string {
return authenticator.generate(secret);
return generateSync({ secret, guardrails });
}
/** Generate a fresh base32 secret for enrolling a new authenticator. */
export function generateTotpSecret(): string {
return authenticator.generateSecret();
return generateSecret();
}
/** otpauth:// URI for provisioning a QR code. */
@@ -29,5 +45,9 @@ export function totpKeyUri(
accountName: string,
issuer: string,
): string {
return authenticator.keyuri(accountName, issuer, secret);
return generateURI({
issuer,
label: accountName,
secret,
});
}
+3 -1
View File
@@ -13,7 +13,9 @@ describe("csp", () => {
expect(csp).toContain("script-src");
expect(csp).toContain("'nonce-testNonce123'");
expect(csp).not.toMatch(/script-src[^;]*'unsafe-inline'/);
expect(csp).toContain("style-src 'self' 'unsafe-inline'");
expect(csp).toContain("style-src 'self' 'nonce-testNonce123'");
expect(csp).not.toMatch(/style-src(?!-attr)[^;]*'unsafe-inline'/);
expect(csp).toContain("style-src-attr 'unsafe-inline'");
expect(csp).toContain("https://challenges.cloudflare.com");
expect(csp).toContain("https://cdn.jsdelivr.net");
});
+11 -5
View File
@@ -1,8 +1,7 @@
/**
* Build a Content-Security-Policy value.
* Scripts: nonce for Next.js / first-party inline; host allowlists for captcha,
* Cloudflare Insights, and TinyMCE CDN. style-src keeps 'unsafe-inline' for
* theme CSS variables (ThemeVars) — nonce styles are a follow-up.
* Scripts/styles: per-request nonce for Next.js and ThemeVars inline tags.
* style-src-attr keeps 'unsafe-inline' so React `style={{…}}` attributes work.
*/
export function buildContentSecurityPolicy(nonce: string): string {
const isDev = process.env.NODE_ENV === "development";
@@ -17,14 +16,21 @@ export function buildContentSecurityPolicy(nonce: string): string {
...(isDev ? ["'unsafe-eval'"] : []),
].join(" ");
const styleSrc = [
"'self'",
`'nonce-${nonce}'`,
"https://fonts.googleapis.com",
].join(" ");
return [
"default-src 'self'",
`script-src ${scriptSrc}`,
"style-src 'self' 'unsafe-inline'",
`style-src ${styleSrc}`,
"style-src-attr 'unsafe-inline'",
"img-src 'self' data: blob: https:",
"frame-src 'self' https://challenges.cloudflare.com https://www.google.com/recaptcha/",
"connect-src 'self' https: wss:",
"font-src 'self' data:",
"font-src 'self' data: https://fonts.gstatic.com",
"object-src 'none'",
"base-uri 'self'",
"form-action 'self'",
-2
View File
@@ -11,8 +11,6 @@ import { prisma } from "./prisma";
// Re-export PERMS from the standalone file (safe for client components)
export { PERMS } from "./permission-slugs";
import { PERMS } from "./permission-slugs";
// ── Permission Set ──────────────────────────────────────────────────
export type { PermissionSet } from "@/types/admin";