mirror of
https://github.com/dani-garcia/vaultwarden.git
synced 2025-09-19 06:55:58 +03:00
Removed unsafe-inline JS from CSP and other fixes
- Removed `unsafe-inline` for javascript from CSP. The admin interface now uses files instead of inline javascript. - Modified javascript to work not being inline. - Run eslint over javascript and fixed some items. - Added a `to_json` Handlebars helper. Used at the diagnostics page. - Changed `AdminTemplateData` struct to be smaller. The `config` was always added, but only used at one page. Same goes for `can_backup` and `version`. - Also inlined CSS. We can't remove the `unsafe-inline` from css, because that seems to break the web-vault currently. That might need some further checks. But for now the 404 page and all the admin pages are clear of inline scripts and styles.
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
<span class="badge bg-info d-none" id="server-branch" title="This is a branched version.">Branched</span>
|
||||
</dt>
|
||||
<dd class="col-sm-7">
|
||||
<span id="server-installed">{{version}}</span>
|
||||
<span id="server-installed">{{page_data.current_release}}</span>
|
||||
</dd>
|
||||
<dt class="col-sm-5">Server Latest
|
||||
<span class="badge bg-secondary d-none" id="server-failed" title="Unable to determine latest version.">Unknown</span>
|
||||
@@ -55,6 +55,10 @@
|
||||
<div class="row">
|
||||
<div class="col-md">
|
||||
<dl class="row">
|
||||
<dt class="col-sm-5">OS/Arch</dt>
|
||||
<dd class="col-sm-7">
|
||||
<span class="d-block"><b>{{ page_data.host_os }} / {{ page_data.host_arch }}</b></span>
|
||||
</dd>
|
||||
<dt class="col-sm-5">Running within Docker</dt>
|
||||
<dd class="col-sm-7">
|
||||
{{#if page_data.running_within_docker}}
|
||||
@@ -140,8 +144,8 @@
|
||||
<span><b>Server:</b> {{page_data.server_time_local}}</span>
|
||||
</dd>
|
||||
<dt class="col-sm-5">Date & Time (UTC)
|
||||
<span class="badge bg-success d-none" id="time-success" title="Server and browser times are within 30 seconds of each other.">Ok</span>
|
||||
<span class="badge bg-danger d-none" id="time-warning" title="Server and browser times are more than 30 seconds apart.">Error</span>
|
||||
<span class="badge bg-success d-none" id="time-success" title="Server and browser times are within 20 seconds of each other.">Ok</span>
|
||||
<span class="badge bg-danger d-none" id="time-warning" title="Server and browser times are more than 20 seconds apart.">Error</span>
|
||||
</dt>
|
||||
<dd class="col-sm-7">
|
||||
<span id="time-server" class="d-block"><b>Server:</b> <span id="time-server-string">{{page_data.server_time}}</span></span>
|
||||
@@ -180,10 +184,10 @@
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-3">
|
||||
<button type="button" id="gen-support" class="btn btn-primary" onclick="generateSupportString(); return false;">Generate Support String</button>
|
||||
<button type="button" id="gen-support" class="btn btn-primary">Generate Support String</button>
|
||||
<br><br>
|
||||
<button type="button" id="copy-support" class="btn btn-info mb-3 d-none" onclick="copyToClipboard(); return false;">Copy To Clipboard</button>
|
||||
<div class="toast-container position-absolute float-start" style="width: 15rem;">
|
||||
<button type="button" id="copy-support" class="btn btn-info mb-3 d-none">Copy To Clipboard</button>
|
||||
<div class="toast-container position-absolute float-start vw-copy-toast">
|
||||
<div id="toastClipboardCopy" class="toast fade hide" role="status" aria-live="polite" aria-atomic="true" data-bs-autohide="true" data-bs-delay="1500">
|
||||
<div class="toast-body">
|
||||
Copied to clipboard!
|
||||
@@ -192,197 +196,12 @@
|
||||
</div>
|
||||
</dt>
|
||||
<dd class="col-sm-9">
|
||||
<pre id="support-string" class="pre-scrollable d-none w-100 border p-2" style="height: 16rem;"></pre>
|
||||
<pre id="support-string" class="pre-scrollable d-none w-100 border p-2"></pre>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
var dnsCheck = false;
|
||||
var timeCheck = false;
|
||||
var domainCheck = false;
|
||||
var httpsCheck = false;
|
||||
|
||||
(() => {
|
||||
// ================================
|
||||
// Date & Time Check
|
||||
const d = new Date();
|
||||
const year = d.getUTCFullYear();
|
||||
const month = String(d.getUTCMonth()+1).padStart(2, '0');
|
||||
const day = String(d.getUTCDate()).padStart(2, '0');
|
||||
const hour = String(d.getUTCHours()).padStart(2, '0');
|
||||
const minute = String(d.getUTCMinutes()).padStart(2, '0');
|
||||
const seconds = String(d.getUTCSeconds()).padStart(2, '0');
|
||||
const browserUTC = `${year}-${month}-${day} ${hour}:${minute}:${seconds} UTC`;
|
||||
document.getElementById("time-browser-string").innerText = browserUTC;
|
||||
|
||||
const serverUTC = document.getElementById("time-server-string").innerText;
|
||||
const timeDrift = (
|
||||
Date.parse(serverUTC.replace(' ', 'T').replace(' UTC', '')) -
|
||||
Date.parse(browserUTC.replace(' ', 'T').replace(' UTC', ''))
|
||||
) / 1000;
|
||||
if (timeDrift > 30 || timeDrift < -30) {
|
||||
document.getElementById('time-warning').classList.remove('d-none');
|
||||
} else {
|
||||
document.getElementById('time-success').classList.remove('d-none');
|
||||
timeCheck = true;
|
||||
}
|
||||
|
||||
// ================================
|
||||
// Check if the output is a valid IP
|
||||
const isValidIp = value => (/^(?:(?:^|\.)(?:2(?:5[0-5]|[0-4]\d)|1?\d?\d)){4}$/.test(value) ? true : false);
|
||||
if (isValidIp(document.getElementById('dns-resolved').innerText)) {
|
||||
document.getElementById('dns-success').classList.remove('d-none');
|
||||
dnsCheck = true;
|
||||
} else {
|
||||
document.getElementById('dns-warning').classList.remove('d-none');
|
||||
}
|
||||
|
||||
// ================================
|
||||
// Version check for both vaultwarden and web-vault
|
||||
let serverInstalled = document.getElementById('server-installed').innerText;
|
||||
let serverLatest = document.getElementById('server-latest').innerText;
|
||||
let serverLatestCommit = document.getElementById('server-latest-commit').innerText.replace('-', '');
|
||||
if (serverInstalled.indexOf('-') !== -1 && serverLatest !== '-' && serverLatestCommit !== '-') {
|
||||
document.getElementById('server-latest-commit').classList.remove('d-none');
|
||||
}
|
||||
|
||||
const webInstalled = document.getElementById('web-installed').innerText;
|
||||
checkVersions('server', serverInstalled, serverLatest, serverLatestCommit);
|
||||
|
||||
{{#unless page_data.running_within_docker}}
|
||||
const webLatest = document.getElementById('web-latest').innerText;
|
||||
checkVersions('web', webInstalled, webLatest);
|
||||
{{/unless}}
|
||||
|
||||
function checkVersions(platform, installed, latest, commit=null) {
|
||||
if (installed === '-' || latest === '-') {
|
||||
document.getElementById(platform + '-failed').classList.remove('d-none');
|
||||
return;
|
||||
}
|
||||
|
||||
// Only check basic versions, no commit revisions
|
||||
if (commit === null || installed.indexOf('-') === -1) {
|
||||
if (installed !== latest) {
|
||||
document.getElementById(platform + '-warning').classList.remove('d-none');
|
||||
} else {
|
||||
document.getElementById(platform + '-success').classList.remove('d-none');
|
||||
}
|
||||
} else {
|
||||
// Check if this is a branched version.
|
||||
const branchRegex = /(?:\s)\((.*?)\)/;
|
||||
const branchMatch = installed.match(branchRegex);
|
||||
if (branchMatch !== null) {
|
||||
document.getElementById(platform + '-branch').classList.remove('d-none');
|
||||
}
|
||||
|
||||
// This will remove branch info and check if there is a commit hash
|
||||
const installedRegex = /(\d+\.\d+\.\d+)-(\w+)/;
|
||||
const instMatch = installed.match(installedRegex);
|
||||
|
||||
// It could be that a new tagged version has the same commit hash.
|
||||
// In this case the version is the same but only the number is different
|
||||
if (instMatch !== null) {
|
||||
if (instMatch[2] === commit) {
|
||||
// The commit hashes are the same, so latest version is installed
|
||||
document.getElementById(platform + '-success').classList.remove('d-none');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (installed === latest) {
|
||||
document.getElementById(platform + '-success').classList.remove('d-none');
|
||||
} else {
|
||||
document.getElementById(platform + '-warning').classList.remove('d-none');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ================================
|
||||
// Check valid DOMAIN configuration
|
||||
document.getElementById('domain-browser-string').innerText = location.href.toLowerCase();
|
||||
if (document.getElementById('domain-server-string').innerText.toLowerCase() == location.href.toLowerCase()) {
|
||||
document.getElementById('domain-success').classList.remove('d-none');
|
||||
domainCheck = true;
|
||||
} else {
|
||||
document.getElementById('domain-warning').classList.remove('d-none');
|
||||
}
|
||||
|
||||
// Check for HTTPS at domain-server-string
|
||||
if (document.getElementById('domain-server-string').innerText.toLowerCase().startsWith('https://') ) {
|
||||
document.getElementById('https-success').classList.remove('d-none');
|
||||
httpsCheck = true;
|
||||
} else {
|
||||
document.getElementById('https-warning').classList.remove('d-none');
|
||||
}
|
||||
})();
|
||||
|
||||
// ================================
|
||||
// Generate support string to be pasted on github or the forum
|
||||
async function generateSupportString() {
|
||||
let supportString = "### Your environment (Generated via diagnostics page)\n";
|
||||
|
||||
supportString += "* Vaultwarden version: v{{ version }}\n";
|
||||
supportString += "* Web-vault version: v{{ page_data.web_vault_version }}\n";
|
||||
supportString += "* Running within Docker: {{ page_data.running_within_docker }} (Base: {{ page_data.docker_base_image }})\n";
|
||||
supportString += "* Environment settings overridden: ";
|
||||
{{#if page_data.overrides}}
|
||||
supportString += "true\n"
|
||||
{{else}}
|
||||
supportString += "false\n"
|
||||
{{/if}}
|
||||
supportString += "* Uses a reverse proxy: {{ page_data.ip_header_exists }}\n";
|
||||
{{#if page_data.ip_header_exists}}
|
||||
supportString += "* IP Header check: {{ page_data.ip_header_match }} ({{ page_data.ip_header_name }})\n";
|
||||
{{/if}}
|
||||
supportString += "* Internet access: {{ page_data.has_http_access }}\n";
|
||||
supportString += "* Internet access via a proxy: {{ page_data.uses_proxy }}\n";
|
||||
supportString += "* DNS Check: " + dnsCheck + "\n";
|
||||
supportString += "* Time Check: " + timeCheck + "\n";
|
||||
supportString += "* Domain Configuration Check: " + domainCheck + "\n";
|
||||
supportString += "* HTTPS Check: " + httpsCheck + "\n";
|
||||
supportString += "* Database type: {{ page_data.db_type }}\n";
|
||||
supportString += "* Database version: {{ page_data.db_version }}\n";
|
||||
supportString += "* Clients used: \n";
|
||||
supportString += "* Reverse proxy and version: \n";
|
||||
supportString += "* Other relevant information: \n";
|
||||
|
||||
let jsonResponse = await fetch('{{urlpath}}/admin/diagnostics/config', {
|
||||
'headers': { 'Accept': 'application/json' }
|
||||
});
|
||||
if (!jsonResponse.ok) {
|
||||
alert("Generation failed: " + jsonResponse.statusText);
|
||||
throw new Error(jsonResponse);
|
||||
}
|
||||
const configJson = await jsonResponse.json();
|
||||
supportString += "\n### Config (Generated via diagnostics page)\n<details><summary>Show Running Config</summary>\n"
|
||||
supportString += "\n**Environment settings which are overridden:** {{page_data.overrides}}\n"
|
||||
supportString += "\n\n```json\n" + JSON.stringify(configJson, undefined, 2) + "\n```\n</details>\n";
|
||||
|
||||
document.getElementById('support-string').innerText = supportString;
|
||||
document.getElementById('support-string').classList.remove('d-none');
|
||||
document.getElementById('copy-support').classList.remove('d-none');
|
||||
}
|
||||
|
||||
function copyToClipboard() {
|
||||
const supportStr = document.getElementById('support-string').innerText;
|
||||
const tmpCopyEl = document.createElement('textarea');
|
||||
|
||||
tmpCopyEl.setAttribute('id', 'copy-support-string');
|
||||
tmpCopyEl.setAttribute('readonly', '');
|
||||
tmpCopyEl.value = supportStr;
|
||||
tmpCopyEl.style.position = 'absolute';
|
||||
tmpCopyEl.style.left = '-9999px';
|
||||
document.body.appendChild(tmpCopyEl);
|
||||
tmpCopyEl.select();
|
||||
document.execCommand('copy');
|
||||
tmpCopyEl.remove();
|
||||
|
||||
new BSN.Toast('#toastClipboardCopy').show();
|
||||
}
|
||||
</script>
|
||||
<script src="{{urlpath}}/vw_static/admin_diagnostics.js"></script>
|
||||
<script type="application/json" id="diagnostics_json">{{to_json page_data}}</script>
|
||||
|
Reference in New Issue
Block a user