fix: relax CSP for /docs page — allow unsafe-eval for Swagger UI 5.x (ajv)
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 8m41s
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 8m41s
Swagger UI 5.x uses new Function() via ajv for JSON schema validation. Helmet default CSP (script-src self) blocks this in Firefox, causing TypeError: NetworkError when attempting to fetch resource on Try It. Override CSP on /docs route to allow unsafe-eval.
This commit is contained in:
parent
a996c76c11
commit
a45d7704ab
3 changed files with 24 additions and 6 deletions
3
dist/index.js
vendored
3
dist/index.js
vendored
|
|
@ -182,6 +182,9 @@ app.use(express.static(path.join(__dirname, "../public"), {
|
||||||
}));
|
}));
|
||||||
// Docs page (clean URL)
|
// Docs page (clean URL)
|
||||||
app.get("/docs", (_req, res) => {
|
app.get("/docs", (_req, res) => {
|
||||||
|
// Swagger UI 5.x uses new Function() (via ajv) for JSON schema validation.
|
||||||
|
// Override helmet's default CSP to allow 'unsafe-eval' + blob: for Swagger UI.
|
||||||
|
res.setHeader("Content-Security-Policy", "default-src 'self';script-src 'self' 'unsafe-eval';style-src 'self' https: 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' https: data:;connect-src 'self';worker-src 'self' blob:;base-uri 'self';form-action 'self';frame-ancestors 'self';object-src 'none'");
|
||||||
res.setHeader('Cache-Control', 'public, max-age=86400');
|
res.setHeader('Cache-Control', 'public, max-age=86400');
|
||||||
res.sendFile(path.join(__dirname, "../public/docs.html"));
|
res.sendFile(path.join(__dirname, "../public/docs.html"));
|
||||||
});
|
});
|
||||||
|
|
|
||||||
22
dist/services/email.js
vendored
22
dist/services/email.js
vendored
|
|
@ -1,18 +1,28 @@
|
||||||
import nodemailer from "nodemailer";
|
import nodemailer from "nodemailer";
|
||||||
import logger from "./logger.js";
|
import logger from "./logger.js";
|
||||||
const transporter = nodemailer.createTransport({
|
const smtpUser = process.env.SMTP_USER;
|
||||||
host: process.env.SMTP_HOST || "host.docker.internal",
|
const smtpPass = process.env.SMTP_PASS;
|
||||||
port: Number(process.env.SMTP_PORT || 25),
|
const smtpHost = process.env.SMTP_HOST || "host.docker.internal";
|
||||||
secure: false,
|
const smtpPort = Number(process.env.SMTP_PORT || 25);
|
||||||
|
const smtpFrom = process.env.SMTP_FROM || "DocFast <noreply@docfast.dev>";
|
||||||
|
const smtpSecure = smtpPort === 465;
|
||||||
|
const transportConfig = {
|
||||||
|
host: smtpHost,
|
||||||
|
port: smtpPort,
|
||||||
|
secure: smtpSecure,
|
||||||
connectionTimeout: 5000,
|
connectionTimeout: 5000,
|
||||||
greetingTimeout: 5000,
|
greetingTimeout: 5000,
|
||||||
socketTimeout: 10000,
|
socketTimeout: 10000,
|
||||||
tls: { rejectUnauthorized: false },
|
tls: { rejectUnauthorized: false },
|
||||||
});
|
};
|
||||||
|
if (smtpUser && smtpPass) {
|
||||||
|
transportConfig.auth = { user: smtpUser, pass: smtpPass };
|
||||||
|
}
|
||||||
|
const transporter = nodemailer.createTransport(transportConfig);
|
||||||
export async function sendVerificationEmail(email, code) {
|
export async function sendVerificationEmail(email, code) {
|
||||||
try {
|
try {
|
||||||
const info = await transporter.sendMail({
|
const info = await transporter.sendMail({
|
||||||
from: "DocFast <noreply@docfast.dev>",
|
from: smtpFrom,
|
||||||
to: email,
|
to: email,
|
||||||
subject: "DocFast - Verify your email",
|
subject: "DocFast - Verify your email",
|
||||||
text: `Your DocFast verification code is: ${code}\n\nThis code expires in 15 minutes.\n\nIf you didn't request this, ignore this email.`,
|
text: `Your DocFast verification code is: ${code}\n\nThis code expires in 15 minutes.\n\nIf you didn't request this, ignore this email.`,
|
||||||
|
|
|
||||||
|
|
@ -199,6 +199,11 @@ app.use(express.static(path.join(__dirname, "../public"), {
|
||||||
|
|
||||||
// Docs page (clean URL)
|
// Docs page (clean URL)
|
||||||
app.get("/docs", (_req, res) => {
|
app.get("/docs", (_req, res) => {
|
||||||
|
// Swagger UI 5.x uses new Function() (via ajv) for JSON schema validation.
|
||||||
|
// Override helmet's default CSP to allow 'unsafe-eval' + blob: for Swagger UI.
|
||||||
|
res.setHeader("Content-Security-Policy",
|
||||||
|
"default-src 'self';script-src 'self' 'unsafe-eval';style-src 'self' https: 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' https: data:;connect-src 'self';worker-src 'self' blob:;base-uri 'self';form-action 'self';frame-ancestors 'self';object-src 'none'"
|
||||||
|
);
|
||||||
res.setHeader('Cache-Control', 'public, max-age=86400');
|
res.setHeader('Cache-Control', 'public, max-age=86400');
|
||||||
res.sendFile(path.join(__dirname, "../public/docs.html"));
|
res.sendFile(path.join(__dirname, "../public/docs.html"));
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue