mirror of
https://github.com/louislam/uptime-kuma.git
synced 2024-11-27 16:54:04 +00:00
Allowed markdown in footer of status page
Markdown support has been added using the marked module. To secure against XSS attacks, DOMPurify is used to sanitize the generated HTML before it is loaded on the page. Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
This commit is contained in:
parent
f3d3e064f8
commit
6bc0bd84af
4 changed files with 42 additions and 1 deletions
28
package-lock.json
generated
28
package-lock.json
generated
|
@ -26,6 +26,7 @@
|
||||||
"compare-versions": "~3.6.0",
|
"compare-versions": "~3.6.0",
|
||||||
"compression": "~1.7.4",
|
"compression": "~1.7.4",
|
||||||
"dayjs": "~1.11.5",
|
"dayjs": "~1.11.5",
|
||||||
|
"dompurify": "^2.4.3",
|
||||||
"express": "~4.17.3",
|
"express": "~4.17.3",
|
||||||
"express-basic-auth": "~1.2.1",
|
"express-basic-auth": "~1.2.1",
|
||||||
"express-static-gzip": "~2.1.7",
|
"express-static-gzip": "~2.1.7",
|
||||||
|
@ -38,6 +39,7 @@
|
||||||
"jsonwebtoken": "~9.0.0",
|
"jsonwebtoken": "~9.0.0",
|
||||||
"jwt-decode": "~3.1.2",
|
"jwt-decode": "~3.1.2",
|
||||||
"limiter": "~2.1.0",
|
"limiter": "~2.1.0",
|
||||||
|
"marked": "^4.2.5",
|
||||||
"mqtt": "~4.3.7",
|
"mqtt": "~4.3.7",
|
||||||
"mssql": "~8.1.4",
|
"mssql": "~8.1.4",
|
||||||
"mysql2": "~2.3.3",
|
"mysql2": "~2.3.3",
|
||||||
|
@ -6499,6 +6501,11 @@
|
||||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dompurify": {
|
||||||
|
"version": "2.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.3.tgz",
|
||||||
|
"integrity": "sha512-q6QaLcakcRjebxjg8/+NP+h0rPfatOgOzc46Fst9VAA3jF2ApfKBNKMzdP4DYTqtUMXSCd5pRS/8Po/OmoCHZQ=="
|
||||||
|
},
|
||||||
"node_modules/domutils": {
|
"node_modules/domutils": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
|
||||||
|
@ -12185,6 +12192,17 @@
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/marked": {
|
||||||
|
"version": "4.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/marked/-/marked-4.2.5.tgz",
|
||||||
|
"integrity": "sha512-jPueVhumq7idETHkb203WDD4fMA3yV9emQ5vLwop58lu8bTclMghBWcYAavlDqIEMaisADinV1TooIFCfqOsYQ==",
|
||||||
|
"bin": {
|
||||||
|
"marked": "bin/marked.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/mathml-tag-names": {
|
"node_modules/mathml-tag-names": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz",
|
||||||
|
@ -22155,6 +22173,11 @@
|
||||||
"domelementtype": "^2.3.0"
|
"domelementtype": "^2.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dompurify": {
|
||||||
|
"version": "2.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.3.tgz",
|
||||||
|
"integrity": "sha512-q6QaLcakcRjebxjg8/+NP+h0rPfatOgOzc46Fst9VAA3jF2ApfKBNKMzdP4DYTqtUMXSCd5pRS/8Po/OmoCHZQ=="
|
||||||
|
},
|
||||||
"domutils": {
|
"domutils": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
|
||||||
|
@ -26299,6 +26322,11 @@
|
||||||
"integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==",
|
"integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"marked": {
|
||||||
|
"version": "4.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/marked/-/marked-4.2.5.tgz",
|
||||||
|
"integrity": "sha512-jPueVhumq7idETHkb203WDD4fMA3yV9emQ5vLwop58lu8bTclMghBWcYAavlDqIEMaisADinV1TooIFCfqOsYQ=="
|
||||||
|
},
|
||||||
"mathml-tag-names": {
|
"mathml-tag-names": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz",
|
||||||
|
|
|
@ -81,6 +81,7 @@
|
||||||
"compare-versions": "~3.6.0",
|
"compare-versions": "~3.6.0",
|
||||||
"compression": "~1.7.4",
|
"compression": "~1.7.4",
|
||||||
"dayjs": "~1.11.5",
|
"dayjs": "~1.11.5",
|
||||||
|
"dompurify": "^2.4.3",
|
||||||
"express": "~4.17.3",
|
"express": "~4.17.3",
|
||||||
"express-basic-auth": "~1.2.1",
|
"express-basic-auth": "~1.2.1",
|
||||||
"express-static-gzip": "~2.1.7",
|
"express-static-gzip": "~2.1.7",
|
||||||
|
@ -93,6 +94,7 @@
|
||||||
"jsonwebtoken": "~9.0.0",
|
"jsonwebtoken": "~9.0.0",
|
||||||
"jwt-decode": "~3.1.2",
|
"jwt-decode": "~3.1.2",
|
||||||
"limiter": "~2.1.0",
|
"limiter": "~2.1.0",
|
||||||
|
"marked": "^4.2.5",
|
||||||
"mqtt": "~4.3.7",
|
"mqtt": "~4.3.7",
|
||||||
"mssql": "~8.1.4",
|
"mssql": "~8.1.4",
|
||||||
"mysql2": "~2.3.3",
|
"mysql2": "~2.3.3",
|
||||||
|
|
|
@ -675,4 +675,5 @@ export default {
|
||||||
"General Monitor Type": "General Monitor Type",
|
"General Monitor Type": "General Monitor Type",
|
||||||
"Passive Monitor Type": "Passive Monitor Type",
|
"Passive Monitor Type": "Passive Monitor Type",
|
||||||
"Specific Monitor Type": "Specific Monitor Type",
|
"Specific Monitor Type": "Specific Monitor Type",
|
||||||
|
markdownSupported: "Markdown syntax supported",
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,6 +26,9 @@
|
||||||
<div class="my-3">
|
<div class="my-3">
|
||||||
<label for="footer-text" class="form-label">{{ $t("Footer Text") }}</label>
|
<label for="footer-text" class="form-label">{{ $t("Footer Text") }}</label>
|
||||||
<textarea id="footer-text" v-model="config.footerText" class="form-control"></textarea>
|
<textarea id="footer-text" v-model="config.footerText" class="form-control"></textarea>
|
||||||
|
<div class="form-text">
|
||||||
|
{{ $t("markdownSupported") }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="my-3 form-check form-switch">
|
<div class="my-3 form-check form-switch">
|
||||||
|
@ -279,7 +282,9 @@
|
||||||
<div class="custom-footer-text text-start">
|
<div class="custom-footer-text text-start">
|
||||||
<strong v-if="enableEditMode">{{ $t("Custom Footer") }}:</strong>
|
<strong v-if="enableEditMode">{{ $t("Custom Footer") }}:</strong>
|
||||||
</div>
|
</div>
|
||||||
<Editable v-model="config.footerText" tag="div" :contenteditable="enableEditMode" :noNL="false" class="alert-heading p-2" />
|
<Editable v-if="enableEditMode" v-model="config.footerText" tag="div" :contenteditable="enableEditMode" :noNL="false" class="alert-heading p-2" />
|
||||||
|
<!-- eslint-disable-next-line vue/no-v-html-->
|
||||||
|
<div v-if="! enableEditMode" class="alert-heading p-2" v-html="footerHTML"></div>
|
||||||
|
|
||||||
<p v-if="config.showPoweredBy">
|
<p v-if="config.showPoweredBy">
|
||||||
{{ $t("Powered by") }} <a target="_blank" rel="noopener noreferrer" href="https://github.com/louislam/uptime-kuma">{{ $t("Uptime Kuma" ) }}</a>
|
{{ $t("Powered by") }} <a target="_blank" rel="noopener noreferrer" href="https://github.com/louislam/uptime-kuma">{{ $t("Uptime Kuma" ) }}</a>
|
||||||
|
@ -310,6 +315,8 @@ import ImageCropUpload from "vue-image-crop-upload";
|
||||||
import { PrismEditor } from "vue-prism-editor";
|
import { PrismEditor } from "vue-prism-editor";
|
||||||
import "vue-prism-editor/dist/prismeditor.min.css"; // import the styles somewhere
|
import "vue-prism-editor/dist/prismeditor.min.css"; // import the styles somewhere
|
||||||
import { useToast } from "vue-toastification";
|
import { useToast } from "vue-toastification";
|
||||||
|
import { marked } from "marked";
|
||||||
|
import DOMPurify from "dompurify";
|
||||||
import Confirm from "../components/Confirm.vue";
|
import Confirm from "../components/Confirm.vue";
|
||||||
import PublicGroupList from "../components/PublicGroupList.vue";
|
import PublicGroupList from "../components/PublicGroupList.vue";
|
||||||
import MaintenanceTime from "../components/MaintenanceTime.vue";
|
import MaintenanceTime from "../components/MaintenanceTime.vue";
|
||||||
|
@ -477,6 +484,9 @@ export default {
|
||||||
return this.overallStatus === STATUS_PAGE_MAINTENANCE;
|
return this.overallStatus === STATUS_PAGE_MAINTENANCE;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
footerHTML() {
|
||||||
|
return DOMPurify.sanitize(marked(this.config.footerText));
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue