fleet/ee/vulnerability-dashboard/views/pages/patch-progress.ejs
Eric b1945b2128
Add fleet-vulnerability-dashboard repo to ee/ folder (#17428)
Closes: https://github.com/fleetdm/confidential/issues/4057

Changes:
- Added the contents of the fleet-vulnerability-dashboard repo to
ee/vulnerability-dashboard
- Added a github workflow to deploy the vulnerability dashboard on
Heroku
- Added a github workflow to test changes to the vulnerability-dashboard
- Updated the website's custom configuration to enable
auto-approvals/review requests to files in the
ee/vulnerability-dashboard folder
2024-03-13 13:06:11 -05:00

324 lines
19 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<div id="patch-progress" v-cloak >
<div purpose="page-container" class="container-fluid">
<div class="d-flex flex-md-row flex-column justify-content-between" style="margin-bottom: 32px;">
<h1 class="mb-3 mb-md-0">Patch progress</h1>
<div class="d-flex flex-row align-items-center">
<p class="mb-0">Team</p>
<select class="custom-select team-select mx-2" v-model="teamApid" @change="changeFilteredTeam()">
<option :value="undefined" selected>All teams</option>
<option v-for="option of teamsToDisplay" :value="option.id">{{option.name}}</option>
</select>
</div>
</div>
<div>
<div class="d-flex flex-row justify-content-between">
<h3>Operating system</h3>
<div @click="clickOpenEditModal('operating-systems')">
<a purpose="edit-button">
<img alt="a small pencil" src="/images/pencil-14x14@2x.png">Edit
</a>
</div>
</div>
<table purpose="patch-progress-table" class="table">
<thead class="thead-light">
<tr>
<th purpose="name-column">Name</th>
<th>Patch progress</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>
<span purpose="show-compliant-versions-button" :class="[showCompliantOperatingSystems ? 'expanded' : '']" @click="showCompliantOperatingSystems = !showCompliantOperatingSystems" v-if="patchProgress.operatingSystem !== undefined"></span>
<span purpose="white-space" v-else></span>
Operating system
</td>
<td v-if="patchProgress.operatingSystem === 'N/A' || patchProgress.operatingSystem === undefined"><p class="text-muted">Not configured</p></td>
<td purpose="progress-column" v-else>
<div class="d-flex flex-row align-items-center">
<div purpose="patch-progress-bar" >
<div :class="[patchProgress.operatingSystem >= 80 && patchProgress.operatingSystem <= 89 ? 'progress-yellow' : patchProgress.operatingSystem <= 80 ? 'progress-red' : patchProgress.operatingSystem >= 90 ? 'progress-green' : '' ]" :style="'width:'+patchProgress.operatingSystem+'%' "></div>
</div>
<p class="mb-0 ml-3 text-nowrap">{{patchProgress.operatingSystem}} %</p>
</div>
</td>
<td purpose="export-column" v-if="patchProgress.operatingSystem !== undefined && patchProgress.operatingSystem !== 'N/A' && patchProgress.operatingSystem !== 100">
<span @click="clickOpenExportModal('operatingSystem')">Export unpatched hosts <img src="/images/icon-download-16x16@2x.png" alt="download"></span>
</td>
<td v-else></td>
</tr>
<tr v-if="showCompliantOperatingSystems" v-for="version in compliantVersionInfo.operatingSystem">
<td purpose="version-name-column">{{version.name}}</td>
<td v-if="version.hostCount !== 0">{{version.hostCount > 1 ? version.hostCount + ' hosts' : version.hostCount + ' host' }}</td>
<td v-else>---</td>
<td></td>
</tr>
</tbody>
</table>
</div>
<div>
<div class="d-flex flex-row justify-content-between">
<h3>Browsers</h3>
<div @click="clickOpenEditModal('browsers')">
<a purpose="edit-button">
<img alt="a small pencil" src="/images/pencil-14x14@2x.png">Edit
</a>
</div>
</div>
<table purpose="patch-progress-table" class="table">
<thead class="thead-light">
<tr>
<th purpose="name-column">Name</th>
<th>Patch compliance</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>
<span purpose="show-compliant-versions-button" :class="[showCompliantChromeVersions ? 'expanded' : '']" @click="showCompliantChromeVersions = !showCompliantChromeVersions" v-if="patchProgress.chrome !== undefined"></span>
<span purpose="white-space" v-else></span>
Google chrome
</td>
<td v-if="patchProgress.chrome === undefined"><p class="text-muted">Not configured</p></td>
<td v-else-if="patchProgress.chrome === 'N/A'"><p class="text-muted">---</p></td>
<td purpose="progress-column" v-else>
<div class="d-flex flex-row align-items-center">
<div purpose="patch-progress-bar" >
<div :class="[patchProgress.chrome >= 80 && patchProgress.chrome <= 89 ? 'progress-yellow' : patchProgress.chrome <= 80 ? 'progress-red' : patchProgress.chrome >= 90 ? 'progress-green' : '' ]" :style="'width:'+patchProgress.chrome+'%' "></div>
</div>
<p class="mb-0 ml-3 text-nowrap">{{patchProgress.chrome}} %</p>
</div>
</td>
<td purpose="export-column" v-if="patchProgress.chrome !== undefined && patchProgress.chrome !== 'N/A' && patchProgress.chrome !== 100">
<span @click="clickOpenExportModal('chrome')">Export unpatched hosts <img src="/images/icon-download-16x16@2x.png" alt="download"></span>
</td>
<td v-else></td>
</tr>
<tr v-if="showCompliantChromeVersions" v-for="version in compliantVersionInfo.chrome">
<td purpose="version-name-column">{{version.name}}</td>
<td v-if="version.hostCount !== 0">{{version.hostCount > 1 ? version.hostCount + ' hosts' : version.hostCount + ' host' }}</td>
<td v-else>---</td>
<td></td>
</tr>
<tr>
<td>
<span purpose="show-compliant-versions-button" :class="[showCompliantSafariVersions ? 'expanded' : '']" @click="showCompliantSafariVersions = !showCompliantSafariVersions" v-if="patchProgress.safari !== undefined"></span>
<span purpose="white-space" v-else></span>
Safari
</td>
<td v-if="patchProgress.safari === undefined"><p class="text-muted">Not configured</p></td>
<td v-else-if="patchProgress.safari === 'N/A'"><p class="text-muted">---</p></td>
<td purpose="progress-column" v-else>
<div class="d-flex flex-row align-items-center">
<div purpose="patch-progress-bar" >
<div :class="[patchProgress.safari >= 80 && patchProgress.safari <= 89 ? 'progress-yellow' : patchProgress.safari <= 80 ? 'progress-red' : patchProgress.safari >= 90 ? 'progress-green' : '' ]" :style="'width:'+patchProgress.safari+'%' "></div>
</div>
<p class="mb-0 ml-3 text-nowrap">{{patchProgress.safari}} %</p>
</div>
</td>
<td purpose="export-column" v-if="patchProgress.safari !== undefined&& patchProgress.safari !== 'N/A' && patchProgress.safari !== 100">
<span @click="clickOpenExportModal('safari')">Export unpatched hosts <img src="/images/icon-download-16x16@2x.png" alt="download"></span>
</td>
<td v-else></td>
</tr>
<tr v-if="showCompliantSafariVersions" v-for="version in compliantVersionInfo.safari">
<td purpose="version-name-column">{{version.name}}</td>
<td v-if="version.hostCount !== 0">{{version.hostCount > 1 ? version.hostCount + ' hosts' : version.hostCount + ' host' }}</td>
<td v-else>---</td>
<td></td>
</tr>
<tr>
<td>
<span purpose="show-compliant-versions-button" :class="[showCompliantFirefoxVersions ? 'expanded' : '']" @click="showCompliantFirefoxVersions = !showCompliantFirefoxVersions" v-if="patchProgress.firefox !== undefined"></span>
<span purpose="white-space" v-else></span>
Mozilla Firefox
</td>
<td v-if="patchProgress.firefox === undefined"><p class="text-muted">Not configured</p></td>
<td v-else-if="patchProgress.firefox === 'N/A'"><p class="text-muted">---</p></td>
<td purpose="progress-column" v-else>
<div class="d-flex flex-row align-items-center">
<div purpose="patch-progress-bar" >
<div :class="[patchProgress.firefox >= 80 && patchProgress.firefox <= 89 ? 'progress-yellow' : patchProgress.firefox <= 80 ? 'progress-red' : patchProgress.firefox >= 90 ? 'progress-green' : '' ]" :style="'width:'+patchProgress.firefox+'%' "></div>
</div>
<p class="mb-0 ml-3 text-nowrap">{{patchProgress.firefox}} %</p>
</div>
</td>
<td purpose="export-column" v-if="patchProgress.firefox !== undefined && patchProgress.firefox !== 'N/A' && patchProgress.firefox !== 100">
<span @click="clickOpenExportModal('firefox')">Export unpatched hosts <img src="/images/icon-download-16x16@2x.png" alt="download"></span>
</td>
<td v-else></td>
</tr>
<tr v-if="showCompliantFirefoxVersions" v-for="version in compliantVersionInfo.firefox">
<td purpose="version-name-column">{{version.name}}</td>
<td v-if="version.hostCount !== 0">{{version.hostCount > 1 ? version.hostCount + ' hosts' : version.hostCount + ' host' }}</td>
<td v-else>---</td>
<td></td>
</tr>
</tbody>
</table>
</div>
<div>
<div class="d-flex flex-row justify-content-between">
<h3>Other critical software</h3>
<div @click="clickOpenEditModal('other-critical-software')">
<a purpose="edit-button">
<img alt="a small pencil" src="/images/pencil-14x14@2x.png">Edit
</a>
</div>
</div>
<table purpose="patch-progress-table" class="table">
<thead class="thead-light">
<tr>
<th purpose="name-column">Name</th>
<th>Patch compliance</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>
<span purpose="show-compliant-versions-button" :class="[showCompliantMicrosoftOfficeVersions ? 'expanded' : '']" @click="showCompliantMicrosoftOfficeVersions = !showCompliantMicrosoftOfficeVersions" v-if="patchProgress.microsoftOffice"></span>
<span purpose="white-space" v-else></span>
Microsoft office
</td>
<td v-if="patchProgress.microsoftOffice === undefined"><p class="text-muted">Not configured</p></td>
<td v-else-if="patchProgress.microsoftOffice === 'N/A'"><p class="text-muted">---</p></td>
<td purpose="progress-column" v-else>
<div class="d-flex flex-row align-items-center">
<div purpose="patch-progress-bar">
<div :class="[patchProgress.microsoftOffice >= 80 && patchProgress.microsoftOffice <= 89 ? 'progress-yellow' : patchProgress.microsoftOffice <= 80 ? 'progress-red' : patchProgress.microsoftOffice >= 90 ? 'progress-green' : '' ]" :style="'width:'+patchProgress.microsoftOffice+'%' "></div>
</div>
<p class="mb-0 ml-3 text-nowrap">{{patchProgress.microsoftOffice}} %</p>
</div>
</td>
<td purpose="export-column" v-if="patchProgress.microsoftOffice !== undefined && patchProgress.microsoftOffice !== 'N/A' && patchProgress.microsoftOffice !== 100">
<span @click="clickOpenExportModal('microsoftOffice')">Export unpatched hosts <img src="/images/icon-download-16x16@2x.png" alt="download"></span>
</td>
<td v-else></td>
</tr>
<tr v-if="showCompliantMicrosoftOfficeVersions" v-for="version in compliantVersionInfo.microsoftOffice">
<td purpose="version-name-column">{{version.name}}</td>
<td v-if="version.hostCount !== 0">{{version.hostCount > 1 ? version.hostCount + ' hosts' : version.hostCount + ' host' }}</td>
<td v-else>---</td>
<td></td>
</tr>
<tr>
<td>
<span purpose="show-compliant-versions-button" :class="[showCompliantFlashVersions ? 'expanded' : ''] [patchProgress.flash ? '' : 'opacity-0']" @click="showCompliantFlashVersions = !showCompliantFlashVersions" v-if="patchProgress.flash"></span>
<span purpose="white-space" v-else></span>
Flash
</td>
<td v-if="patchProgress.flash === undefined"><p class="text-muted">Not configured</p></td>
<td v-else-if="patchProgress.flash === 'N/A'"><p class="text-muted">---</p></td>
<td purpose="progress-column" v-else>
<div class="d-flex flex-row align-items-center">
<div purpose="patch-progress-bar" >
<div :class="[patchProgress.flash >= 80 && patchProgress.flash <= 89 ? 'progress-yellow' : patchProgress.flash <= 80 ? 'progress-red' : patchProgress.flash >= 90 ? 'progress-green' : '' ]" :style="'width:'+patchProgress.flash+'%' "></div>
</div>
<p class="mb-0 ml-3 text-nowrap">{{patchProgress.flash}} %</p>
</div>
</td>
<td purpose="export-column" v-if="patchProgress.flash !== undefined && patchProgress.flash !== 'N/A' && patchProgress.flash !== 100">
<span @click="clickOpenExportModal('flash')">Export unpatched hosts <img src="/images/icon-download-16x16@2x.png" alt="download"></span>
</td>
<td v-else></td>
</tr>
<tr v-if="showCompliantFlashVersions" v-for="version in compliantVersionInfo.flash">
<td purpose="version-name-column">{{version.name}}</td>
<td v-if="version.hostCount !== 0">{{version.hostCount > 1 ? version.hostCount + ' hosts' : version.hostCount + ' host' }}</td>
<td v-else>---</td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
<modal v-if="modal === 'operating-systems'" @close="clickCloseModal">
<div class="modal-body">
<h1>Operating systems</h1>
<p>Configure the list of OS versions that meet your organizations security requirements.</p>
<ajax-form :handle-submitting="handleSubmittingOperatingSystemsForm" :syncing.sync="syncing" :cloud-error.sync="cloudError" :form-errors.sync="formErrors" :form-data="formData" :form-rules="formRules">
<div purpose="form-card" class="card">
<p class="form-label">OS versions</p>
<multifield :value="formData.operatingSystem" v-model="formData.operatingSystem" input-type="nameAndHostCountSelect" :select-options="versionsUsedByEntireOrg.operatingSystem"></multifield>
</div>
<cloud-error :cloud-error="cloudError" v-if="cloudError"></cloud-error>
<p class="muted">Not the versions youre looking for? These currently-installed versions are refreshed hourly.</p>
<div class="d-flex flex-row justify-content-end align-items-center">
<a style="cursor: pointer; color: #6A67FE; margin-right: 16px;" @click="clickCloseModal()">Cancel</a>
<ajax-button :syncing="syncing">Save</ajax-button>
</div>
</ajax-form>
</div>
</modal>
<modal v-if="modal === 'browsers'" @close="clickCloseModal">
<div class="modal-body">
<h1>Browsers</h1>
<p>Configure the list of browser versions that meet your organizations security requirements.</p>
<ajax-form :handle-submitting="handleSubmittingBrowsersForm" :syncing.sync="syncing" :cloud-error.sync="cloudError" :form-errors.sync="formErrors" :form-data="formData" :form-rules="formRules">
<div purpose="form-card" class="card">
<p class="form-label">Google Chrome versions</p>
<multifield :value="formData.chrome" v-model="formData.chrome" input-type="nameAndHostCountSelect" :select-options="versionsUsedByEntireOrg.chrome"></multifield>
</div>
<div purpose="form-card" class="card">
<p class="form-label">Firefox versions</p>
<multifield :value="formData.firefox" v-model="formData.firefox" input-type="nameAndHostCountSelect" :select-options="versionsUsedByEntireOrg.firefox"></multifield>
</div>
<div purpose="form-card" class="card">
<p class="form-label">Safari versions</p>
<multifield :value="formData.safari" v-model="formData.safari" input-type="nameAndHostCountSelect" :select-options="versionsUsedByEntireOrg.safari"></multifield>
</div>
<cloud-error :cloud-error="cloudError" v-if="cloudError"></cloud-error>
<p class="muted">Not the versions youre looking for? These currently-installed versions are refreshed hourly.</p>
<div class="d-flex flex-row justify-content-end align-items-center">
<a style="cursor: pointer; color: #6A67FE; margin-right: 16px;" @click="clickCloseModal()">Cancel</a>
<ajax-button :syncing="syncing">Save</ajax-button>
</div>
</ajax-form>
</div>
</modal>
<modal v-if="modal === 'other-critical-software'" @close="clickCloseModal">
<div class="modal-body">
<h1>Other critical software</h1>
<p>Configure the list of critical software versions that meet your organizations security requirements.</p>
<ajax-form :handle-submitting="handleSubmittingOtherCriticalSoftwareForm" :syncing.sync="syncing" :cloud-error.sync="cloudError" :form-errors.sync="formErrors" :form-data="formData" :form-rules="formRules">
<div purpose="form-card" class="card">
<p class="form-label">Microsoft Office versions</p>
<multifield :value="formData.microsoftOffice" v-model="formData.microsoftOffice" input-type="nameAndHostCountSelect" :select-options="versionsUsedByEntireOrg.microsoftOffice"></multifield>
</div>
<div purpose="form-card" class="card">
<p class="form-label">Flash versions</p>
<multifield :value="formData.flash" v-model="formData.flash" input-type="nameAndHostCountSelect" :select-options="versionsUsedByEntireOrg.flash"></multifield>
</div>
<cloud-error :cloud-error="cloudError" v-if="cloudError"></cloud-error>
<p class="muted">Not the versions youre looking for? These currently-installed versions are refreshed hourly.</p>
<div class="d-flex flex-row justify-content-end align-items-center">
<a style="cursor: pointer; color: #6A67FE; margin-right: 16px;" @click="clickCloseModal()">Cancel</a>
<ajax-button :syncing="syncing">Save</ajax-button>
</div>
</ajax-form>
</div>
</modal>
<modal v-if="modal === 'export'" @close="clickCloseModal">
<div purpose="export-modal" class="modal-body">
<h1>Export unpatched hosts</h1>
<p v-if="teamApid !== undefined">This will export a CSV report of hosts on the <strong>{{selectedTeamDisplayName}}</strong> team that are on an unpatched <strong>{{_.startCase(formData.exportType)}}</strong> version.</p>
<p v-else="teamApid">This will export a CSV report of hosts on <strong>all teams</strong> that are on an unpatched <strong>{{_.startCase(formData.exportType)}}</strong> version.</p>
<ajax-form :handle-submitting="handleSubmittingExportForm" :cloud-error.sync="cloudError" :form-errors.sync="formErrors" :form-data="formData" :form-rules="formRules">
<div class="d-flex flex-row justify-content-end align-items-center">
<a class="pr-3" @click="clickCloseModal">Cancel</a>
<ajax-button :syncing="syncing">Export CSV</ajax-button>
</div>
</ajax-form>
</div>
</modal>
<ajax-overlay :syncing-message="syncingMessage" :syncing="syncing"></ajax-overlay>
</div>
<%- /* Expose server-rendered data as window.SAILS_LOCALS :: */ exposeLocalsToBrowser() %>