Skip to content

Commit

Permalink
feat: syntax highlight, foldable text, sanitised, markdown support
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisdlangton committed Nov 24, 2024
1 parent a99b8e0 commit 8cb9ca4
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 7 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@
"axios": "^1.3.1",
"chart.js": "^4.1.2",
"crypto-es": "^2.1.0",
"dompurify": "^3.2.1",
"eslint-import-resolver-alias": "^1.1.2",
"jwt-decode": "^3.1.2",
"marked": "^15.0.2",
"pinia": "^2.1.3",
"prismjs": "^1.29.0",
"roboto-fontface": "^0.10.0",
Expand Down
33 changes: 28 additions & 5 deletions src/components/Finding.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script setup>
import TruncatableText from '@/components/TruncatableText.vue';
import {
getPastelColor,
getSemVerWithoutOperator,
Expand All @@ -11,6 +12,9 @@ import {
} from '@/utils';
import { CVSS31, CVSS40 } from '@pandatix/js-cvss';
import VCodeBlock from '@wdns/vue-code-block';
import DOMPurify from 'dompurify';
import hljs from 'highlight.js';
import { marked } from 'marked';
import { onMounted } from 'vue';
import { useTheme } from 'vuetify';
Expand Down Expand Up @@ -106,6 +110,17 @@ const init = () => {
}))
}
DOMPurify.addHook('afterSanitizeElements', (currentNode, hookEvent, config) => {
if (currentNode?.nodeName === "CODE") {
currentNode.innerHTML = hljs.highlightAuto(currentNode.innerText).value
}
})
const md = (input) => {
const dirty = marked(input)
return DOMPurify.sanitize(dirty, { FORBID_TAGS: ['style', 'script'], USE_PROFILES: { html: true } });
}
const scoreColor = computed(() => {
if (!props.currentTriage?.epssScore) return;
if (props.currentTriage.epssScore < 0.01) return 'success'
Expand Down Expand Up @@ -923,7 +938,13 @@ watch([
</VChip>
</div>
<div class="d-flex flex-wrap gap-2 mt-2">
{{ props.finding.detectionDescription }}
<TruncatableText :maxHeight="60">
<div
style="white-space: preserve-breaks;"
v-html="md(props.finding.detectionDescription)"
>
</div>
</TruncatableText>
</div>
</VCol>
<VCol
Expand Down Expand Up @@ -1173,7 +1194,6 @@ watch([
autodetect
highlightjs
code-block-radius="1em"
:cssPath="global.name.value === 'dark' ? 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/atom-one-dark.min.css' : 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/atom-one-light.min.css'"
:theme="global.name.value === 'dark' ? 'atom-one-dark' : 'atom-one-light'"
v-if="props.finding?.affectedFunctions"
:code="props.finding.affectedFunctions"
Expand Down Expand Up @@ -2495,7 +2515,7 @@ watch([
class="font-weight-bold"
>{{
cvssScore
}} / 10.0</span>
}} / 10.0</span>
</div>
<VProgressLinear
:model-value="cvssScore"
Expand All @@ -2515,7 +2535,7 @@ watch([
<span class="font-weight-medium">EPSS Score</span>
<span class="font-monospace">{{
parseFloat(props.currentTriage.epssScore).toFixed(5)
}}</span>
}}</span>
</div>
<VProgressLinear
:model-value="parseFloat(props.currentTriage.epssScore).toFixed(5)"
Expand All @@ -2536,7 +2556,7 @@ watch([
<span class="font-weight-medium">EPSS Percentile</span>
<span class="font-monospace">{{
parseFloat(props.currentTriage.epssPercentile).toFixed(5)
}}%</span>
}}%</span>
</div>
<VProgressLinear
:model-value="parseFloat(props.currentTriage.epssPercentile).toFixed(5)"
Expand Down Expand Up @@ -2695,6 +2715,9 @@ watch([
</template>
<style scoped>
@import 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/atom-one-dark.min.css';
@import 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/atom-one-light.min.css';
.text-capitalize {
text-transform: capitalize;
}
Expand Down
63 changes: 63 additions & 0 deletions src/components/TruncatableText.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<template>
<span
class="text-wrapper"
ref="textWrapper"
>
<span
class="text-content"
:style="contentStyle"
>
<slot></slot>
</span>
<VBtn
v-if="isTruncated"
class="text-none"
variant="text"
density="compact"
text="read more&mldr;"
small
slim
@click="isTruncated = false"
/>
</span>
</template>

<script>
export default {
name: 'TruncatableText',
props: {
maxHeight: {
type: Number,
default: 100 // Approximately 4 lines of text with default line height
}
},
data() {
return {
isTruncated: true,
originalHeight: 0
}
},
computed: {
contentStyle() {
if (!this.isTruncated) {
return {}
}
return {
maxHeight: `${this.maxHeight}px`,
overflow: 'hidden'
}
}
}
}
</script>

<style scoped>
.text-wrapper {
position: relative;
}
.text-content {
display: inline-block;
transition: max-height 1s ease-out;
}
</style>
2 changes: 0 additions & 2 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,9 @@ import { createApp } from 'vue'

loadFonts()


// Create vue app
const app = createApp(App)


// Use plugins
app.use(vuetify)
app.use(createPinia())
Expand Down
30 changes: 30 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2825,6 +2825,13 @@ __metadata:
languageName: node
linkType: hard

"@types/trusted-types@npm:^2.0.7":
version: 2.0.7
resolution: "@types/trusted-types@npm:2.0.7"
checksum: 10c0/4c4855f10de7c6c135e0d32ce462419d8abbbc33713b31d294596c0cc34ae1fa6112a2f9da729c8f7a20707782b0d69da3b1f8df6645b0366d08825ca1522e0c
languageName: node
linkType: hard

"@types/web-bluetooth@npm:^0.0.20":
version: 0.0.20
resolution: "@types/web-bluetooth@npm:0.0.20"
Expand Down Expand Up @@ -4552,6 +4559,18 @@ __metadata:
languageName: node
linkType: hard

"dompurify@npm:^3.2.1":
version: 3.2.1
resolution: "dompurify@npm:3.2.1"
dependencies:
"@types/trusted-types": "npm:^2.0.7"
dependenciesMeta:
"@types/trusted-types":
optional: true
checksum: 10c0/c40c441813071ebdc06f843dce6f22109d30f14525e710c8b117ef54430358617d4e7910a26fc09b387aa5890d1f510b242b605b0282ed1fb7a4d5104c75e223
languageName: node
linkType: hard

"domutils@npm:^2.8.0":
version: 2.8.0
resolution: "domutils@npm:2.8.0"
Expand Down Expand Up @@ -7138,6 +7157,15 @@ __metadata:
languageName: node
linkType: hard

"marked@npm:^15.0.2":
version: 15.0.2
resolution: "marked@npm:15.0.2"
bin:
marked: bin/marked.js
checksum: 10c0/6c288ac2b41f1b75671b6a06eea4d5f37bf49d42e93a4a175a7e0331d2fc339cdaef937429cf5e4d0e85b2a65926385cab28bdb4c90ac9f96fdc52cab4018466
languageName: node
linkType: hard

"mathml-tag-names@npm:^2.1.3":
version: 2.1.3
resolution: "mathml-tag-names@npm:2.1.3"
Expand Down Expand Up @@ -10177,6 +10205,7 @@ __metadata:
boxicons: "npm:^2.1.4"
chart.js: "npm:^4.1.2"
crypto-es: "npm:^2.1.0"
dompurify: "npm:^3.2.1"
eslint: "npm:^8.41.0"
eslint-config-airbnb-base: "npm:^15.0.0"
eslint-import-resolver-alias: "npm:^1.1.2"
Expand All @@ -10187,6 +10216,7 @@ __metadata:
eslint-plugin-unicorn: "npm:^47.0.0"
eslint-plugin-vue: "npm:^9.13.0"
jwt-decode: "npm:^3.1.2"
marked: "npm:^15.0.2"
pinia: "npm:^2.1.3"
postcss-html: "npm:^1.5.0"
prisma: "npm:^5.21.1"
Expand Down

0 comments on commit 8cb9ca4

Please sign in to comment.