Content-Security-Policy-Report-Onlyを追加

This commit is contained in:
Ry0taK 2023-02-11 21:20:45 +09:00
parent ddde68ad2d
commit b888c66ca2
11 changed files with 76 additions and 56 deletions

View File

@ -144,3 +144,6 @@ signToActivityPubGet: true
# Upload or download file size limits (bytes) # Upload or download file size limits (bytes)
#maxFileSize: 262144000 #maxFileSize: 262144000
# Value of Content-Security-Policy header
#contentSecurityPolicy: "script-src 'self' 'unsafe-eval' https://challenges.cloudflare.com https://hcaptcha.com https://*.hcaptcha.com https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ https://www.recaptcha.net/recaptcha/; base-uri 'self'; object-src 'self';"

View File

@ -145,3 +145,6 @@ signToActivityPubGet: true
# Upload or download file size limits (bytes) # Upload or download file size limits (bytes)
#maxFileSize: 262144000 #maxFileSize: 262144000
# Value of Content-Security-Policy header
#contentSecurityPolicy: "script-src 'self' 'unsafe-eval' https://challenges.cloudflare.com https://hcaptcha.com https://*.hcaptcha.com https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ https://www.recaptcha.net/recaptcha/; base-uri 'self'; object-src 'self';"

View File

@ -36,12 +36,18 @@ gulp.task('copy:frontend:locales', cb => {
}); });
gulp.task('build:backend:script', () => { gulp.task('build:backend:script', () => {
return gulp.src(['./packages/backend/src/server/web/boot.js', './packages/backend/src/server/web/bios.js', './packages/backend/src/server/web/cli.js']) const clientManifestExists = fs.existsSync('./built/_vite_/manifest.json');
const clientEntry = clientManifestExists ?
JSON.parse(fs.readFileSync('./built/_vite_/manifest.json', 'utf-8'))['src/init.ts'].file
: 'src/init.ts'
return gulp.src(['./packages/backend/src/server/web/boot.js', './packages/backend/src/server/web/bios.js', './packages/backend/src/server/web/cli.js', './packages/backend/src/server/web/flush.js'])
.pipe(replace('LANGS', JSON.stringify(Object.keys(locales)))) .pipe(replace('LANGS', JSON.stringify(Object.keys(locales))))
.pipe(replace('CLIENT_ENTRY', JSON.stringify(clientEntry)))
.pipe(terser({ .pipe(terser({
toplevel: true toplevel: true
})) }))
.pipe(gulp.dest('./packages/backend/built/server/web/')); .pipe(gulp.dest('./built/_frontend_dist_/'));
}); });
gulp.task('build:backend:style', () => { gulp.task('build:backend:style', () => {

View File

@ -48,6 +48,8 @@ export type Source = {
allowedPrivateNetworks?: string[]; allowedPrivateNetworks?: string[];
contentSecurityPolicy?: string;
maxFileSize?: number; maxFileSize?: number;
accesslog?: string; accesslog?: string;

View File

@ -172,6 +172,14 @@ export class ClientServerService {
fastify.addHook('onRequest', (request, reply, done) => { fastify.addHook('onRequest', (request, reply, done) => {
// クリックジャッキング防止のためiFrameの中に入れられないようにする // クリックジャッキング防止のためiFrameの中に入れられないようにする
reply.header('X-Frame-Options', 'DENY'); reply.header('X-Frame-Options', 'DENY');
// XSSが存在した場合に影響を軽減する
// (script-srcにunsafe-inline等を追加すると意味が無くなるので注意)
const csp = this.config.contentSecurityPolicy
?? 'script-src \'self\' \'unsafe-eval\' ' +
'https://challenges.cloudflare.com https://hcaptcha.com https://*.hcaptcha.com https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ https://www.recaptcha.net/recaptcha/; ' +
'base-uri \'self\'; object-src \'self\';';
reply.header('Content-Security-Policy', csp);
done(); done();
}); });

View File

@ -154,7 +154,7 @@
<path d="M5 19h14a2 2 0 0 0 1.84 -2.75l-7.1 -12.25a2 2 0 0 0 -3.5 0l-7.1 12.25a2 2 0 0 0 1.75 2.75"></path> <path d="M5 19h14a2 2 0 0 0 1.84 -2.75l-7.1 -12.25a2 2 0 0 0 -3.5 0l-7.1 12.25a2 2 0 0 0 1.75 2.75"></path>
</svg> </svg>
<h1>An error has occurred!</h1> <h1>An error has occurred!</h1>
<button class="button-big" onclick="location.reload();"> <button class="button-big" id="reload">
<span class="button-label-big">Refresh</span> <span class="button-label-big">Refresh</span>
</button> </button>
<p class="dont-worry">Don't worry, it's (probably) not your fault.</p> <p class="dont-worry">Don't worry, it's (probably) not your fault.</p>
@ -181,6 +181,9 @@
<br> <br>
<div id="errors"></div> <div id="errors"></div>
`; `;
document.getElementById("reload").addEventListener('click', () => {
location.reload();
});
errorsElement = document.getElementById('errors'); errorsElement = document.getElementById('errors');
} }
const detailsElement = document.createElement('details'); const detailsElement = document.createElement('details');

View File

@ -0,0 +1,44 @@
'use strict';
const msg = document.getElementById('msg');
const successText = `\nSuccess Flush! <a href="/">Back to Misskey</a>\n成功しました。<a href="/">Misskeyを開き直してください。</a>`;
message('Start flushing.');
(async function () {
try {
localStorage.clear();
message('localStorage cleared.');
const idbPromises = ['MisskeyClient', 'keyval-store'].map((name, i, arr) => new Promise((res, rej) => {
const delidb = indexedDB.deleteDatabase(name);
delidb.onsuccess = () => res(message(`indexedDB "${name}" cleared. (${i + 1}/${arr.length})`));
delidb.onerror = e => rej(e)
}));
await Promise.all(idbPromises);
if (navigator.serviceWorker.controller) {
navigator.serviceWorker.controller.postMessage('clear');
await navigator.serviceWorker.getRegistrations()
.then(registrations => {
return Promise.all(registrations.map(registration => registration.unregister()));
})
.catch(e => { throw new Error(e) });
}
message(successText);
} catch (e) {
message(`\n${e}\n\nFlush Failed. <a href="/flush">Please retry.</a>\n失敗しました。<a href="/flush">もう一度試してみてください。</a>`);
message(`\nIf you retry more than 3 times, clear the browser cache or contact to instance admin.\n3回以上試しても失敗する場合、ブラウザのキャッシュを消去し、それでもだめならインスタンス管理者に連絡してみてください。\n`)
console.error(e);
setTimeout(() => {
location = '/';
}, 10000)
}
})();
function message(text) {
msg.insertAdjacentHTML('beforeend', `<p>[${(new Date()).toString()}] ${text.replace(/\n/g, '<br>')}</p>`)
}

View File

@ -63,12 +63,7 @@ html
style style
include ../style.css include ../style.css
script. script(src=`/assets/boot.js?v=${version}`)
var VERSION = "#{version}";
var CLIENT_ENTRY = "#{clientEntry.file}";
script
include ../boot.js
body body
noscript: p noscript: p

View File

@ -8,8 +8,7 @@ html
title Misskey Repair Tool title Misskey Repair Tool
style style
include ../bios.css include ../bios.css
script script(src=`/assets/bios.js?v=${version}`)
include ../bios.js
body body
header header

View File

@ -8,8 +8,7 @@ html
title Misskey Cli title Misskey Cli
style style
include ../cli.css include ../cli.css
script script(src=`/assets/cli.js?v=${version}`)
include ../cli.js
body body
header header

View File

@ -2,46 +2,4 @@ doctype html
html html
#msg #msg
script. script(src=`/assets/flush.js?v=${version}`)
const msg = document.getElementById('msg');
const successText = `\nSuccess Flush! <a href="/">Back to Misskey</a>\n成功しました。<a href="/">Misskeyを開き直してください。</a>`;
message('Start flushing.');
(async function() {
try {
localStorage.clear();
message('localStorage cleared.');
const idbPromises = ['MisskeyClient', 'keyval-store'].map((name, i, arr) => new Promise((res, rej) => {
const delidb = indexedDB.deleteDatabase(name);
delidb.onsuccess = () => res(message(`indexedDB "${name}" cleared. (${i + 1}/${arr.length})`));
delidb.onerror = e => rej(e)
}));
await Promise.all(idbPromises);
if (navigator.serviceWorker.controller) {
navigator.serviceWorker.controller.postMessage('clear');
await navigator.serviceWorker.getRegistrations()
.then(registrations => {
return Promise.all(registrations.map(registration => registration.unregister()));
})
.catch(e => { throw new Error(e) });
}
message(successText);
} catch (e) {
message(`\n${e}\n\nFlush Failed. <a href="/flush">Please retry.</a>\n失敗しました。<a href="/flush">もう一度試してみてください。</a>`);
message(`\nIf you retry more than 3 times, clear the browser cache or contact to instance admin.\n3回以上試しても失敗する場合、ブラウザのキャッシュを消去し、それでもだめならインスタンス管理者に連絡してみてください。\n`)
console.error(e);
setTimeout(() => {
location = '/';
}, 10000)
}
})();
function message(text) {
msg.insertAdjacentHTML('beforeend', `<p>[${(new Date()).toString()}] ${text.replace(/\n/g,'<br>')}</p>`)
}