mirror of
https://github.com/bookorbit/bookorbit.git
synced 2026-11-03 08:21:39 -06:00
fix(server): allow google translate origin in content security policy
Add translate.googleapis.com to the connect-src Content-Security-Policy directive so the built-in eBook reader's translation feature can perform fetch requests without being blocked by CSP. Closes #786
This commit is contained in:
@@ -179,6 +179,12 @@ describe('buildCspDirectives', () => {
|
||||
expect(connectSrc).toContain('https://api.dictionaryapi.dev');
|
||||
expect(connectSrc).toContain('https://*.wiktionary.org');
|
||||
});
|
||||
|
||||
it('includes the Google Translate endpoint used by the epub reader regardless of Cloudflare Insights setting', () => {
|
||||
expect(buildCspDirectives({ allowCloudflareInsights: false }).connectSrc).toContain('https://translate.googleapis.com');
|
||||
expect(buildCspDirectives({ allowCloudflareInsights: true }).connectSrc).toContain('https://translate.googleapis.com');
|
||||
expect(buildCspDirectives().connectSrc).toContain('https://translate.googleapis.com');
|
||||
});
|
||||
});
|
||||
|
||||
describe('scriptSrc with Cloudflare Insights', () => {
|
||||
@@ -249,6 +255,24 @@ describe('buildCspDirectives', () => {
|
||||
expect(upgradeInsecureRequests).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildHelmetOptions (integration)', () => {
|
||||
it('emits a Content-Security-Policy header whose connect-src allows the Google Translate endpoint', async () => {
|
||||
await withCspApp(async (app) => {
|
||||
const response = await app.inject({ method: 'GET', url: '/ping' });
|
||||
|
||||
const csp = response.headers['content-security-policy'];
|
||||
expect(csp).toBeDefined();
|
||||
|
||||
const connectSrcDirective = String(csp)
|
||||
.split(';')
|
||||
.map((directive) => directive.trim())
|
||||
.find((directive) => directive.startsWith('connect-src'));
|
||||
|
||||
expect(connectSrcDirective).toContain('https://translate.googleapis.com');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('empty request body bootstrap handling', () => {
|
||||
@@ -463,6 +487,19 @@ async function withHstsApp(options: { trustProxy: boolean }, run: (app: FastifyI
|
||||
}
|
||||
}
|
||||
|
||||
async function withCspApp(run: (app: FastifyInstance) => Promise<void>): Promise<void> {
|
||||
const app = Fastify({ logger: false });
|
||||
await app.register(fastifyHelmet, buildHelmetOptions());
|
||||
app.get('/ping', () => ({ ok: true }));
|
||||
|
||||
try {
|
||||
await app.ready();
|
||||
await run(app);
|
||||
} finally {
|
||||
await app.close();
|
||||
}
|
||||
}
|
||||
|
||||
async function withParserApp(run: (app: FastifyInstance) => Promise<void>): Promise<void> {
|
||||
const app = Fastify({ logger: false });
|
||||
registerEmptyBodyContentTypeParser(app);
|
||||
|
||||
@@ -41,6 +41,7 @@ export interface CspOptions {
|
||||
const CLOUDFLARE_INSIGHTS_SCRIPT_SRC = 'https://static.cloudflareinsights.com';
|
||||
const CLOUDFLARE_INSIGHTS_CONNECT_SRC = 'https://cloudflareinsights.com';
|
||||
const DICTIONARY_CONNECT_SRC = ['https://api.dictionaryapi.dev', 'https://*.wiktionary.org'];
|
||||
const TRANSLATE_CONNECT_SRC = 'https://translate.googleapis.com';
|
||||
|
||||
export function buildCspDirectives(options: CspOptions = {}) {
|
||||
const { allowCloudflareInsights = false } = options;
|
||||
@@ -52,6 +53,7 @@ export function buildCspDirectives(options: CspOptions = {}) {
|
||||
'wss:',
|
||||
'https://cdn.jsdelivr.net',
|
||||
...DICTIONARY_CONNECT_SRC,
|
||||
TRANSLATE_CONNECT_SRC,
|
||||
...(allowCloudflareInsights ? [CLOUDFLARE_INSIGHTS_CONNECT_SRC] : []),
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user