mirror of
https://github.com/fleetdm/fleet
synced 2026-05-06 06:48:54 +00:00
Query library improvements (#945)
* Add contributor avatars to query-detail page * Add check for contributors; style elements * Add GitHub avatars, style css, reorder page script * Add os logos, adjust styles * Add mobile styles, refactor scripts, prettier * Update img paths, fix linting errors
This commit is contained in:
parent
2e679a9fcf
commit
71f53e6f4e
8 changed files with 450 additions and 81 deletions
BIN
website/assets/images/chevron-down-9x6@2x.png
vendored
Normal file
BIN
website/assets/images/chevron-down-9x6@2x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 332 B |
BIN
website/assets/images/icon-search-16x16@2x.png
vendored
Normal file
BIN
website/assets/images/icon-search-16x16@2x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 814 B |
32
website/assets/js/pages/query-detail.page.js
vendored
32
website/assets/js/pages/query-detail.page.js
vendored
|
|
@ -3,23 +3,45 @@ parasails.registerPage('query-detail', {
|
|||
// ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣
|
||||
// ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝
|
||||
data: {
|
||||
//…
|
||||
contributors: [],
|
||||
},
|
||||
|
||||
// ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗
|
||||
// ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣
|
||||
// ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝
|
||||
beforeMount: function() {
|
||||
beforeMount: function () {
|
||||
//…
|
||||
},
|
||||
mounted: async function() {
|
||||
//…
|
||||
mounted: async function () {
|
||||
if (this.query && this.query.contributors) {
|
||||
this.contributors = await Promise.all(
|
||||
this.query.contributors
|
||||
.split(',')
|
||||
.map(async (contributor) => this.getGitHubUserData(contributor))
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
// ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
|
||||
// ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗
|
||||
// ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝
|
||||
methods: {
|
||||
getGitHubUserData: async function (userName) {
|
||||
const url =
|
||||
'https://api.github.com/users/' + encodeURIComponent(userName);
|
||||
|
||||
}
|
||||
return await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
},
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.catch((error) => console.log(error));
|
||||
},
|
||||
|
||||
clickAvatar: function (contributor) {
|
||||
window.location = contributor.html_url;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
148
website/assets/js/pages/query-library.page.js
vendored
148
website/assets/js/pages/query-library.page.js
vendored
|
|
@ -3,6 +3,7 @@ parasails.registerPage('query-library', {
|
|||
// ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣
|
||||
// ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝
|
||||
data: {
|
||||
contributorsDictionary: {},
|
||||
inputTextValue: '',
|
||||
inputTimers: {},
|
||||
searchString: '', // The user input string to be searched against the query library
|
||||
|
|
@ -12,57 +13,71 @@ parasails.registerPage('query-library', {
|
|||
|
||||
computed: {
|
||||
filteredQueries: function () {
|
||||
return _.filter(this.queries, (query) => this.isIncluded(query.platforms, this.selectedPlatform) && this.isIncluded(query.purpose, this.selectedPurpose));
|
||||
return this.queries.filter(
|
||||
(query) =>
|
||||
this._isIncluded(query.platforms, this.selectedPlatform) &&
|
||||
this._isIncluded(query.purpose, this.selectedPurpose)
|
||||
);
|
||||
},
|
||||
|
||||
searchResults: function () {
|
||||
return this.search(this.filteredQueries, this.searchString);
|
||||
return this._search(this.filteredQueries, this.searchString);
|
||||
},
|
||||
|
||||
queriesList: function () {
|
||||
return this.searchResults;
|
||||
}
|
||||
|
||||
},
|
||||
},
|
||||
|
||||
// ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗
|
||||
// ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣
|
||||
// ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝
|
||||
beforeMount: function() {
|
||||
beforeMount: function () {
|
||||
//…
|
||||
},
|
||||
mounted: async function() {
|
||||
//…
|
||||
mounted: async function () {
|
||||
const uniqueContributors = this._getUniqueContributors(this.queries);
|
||||
this.contributorsDictionary = Object.assign(
|
||||
{},
|
||||
await this._threadGitHubAPICalls(uniqueContributors)
|
||||
);
|
||||
},
|
||||
|
||||
// ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
|
||||
// ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗
|
||||
// ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝
|
||||
methods: {
|
||||
clickCard: function (querySlug) {
|
||||
window.location = '/queries/' + querySlug; // we can trust the query slug is url-safe
|
||||
},
|
||||
|
||||
isIncluded: function (queryProperty, selectedOption) {
|
||||
if (selectedOption.startsWith('all') || selectedOption === '') {
|
||||
return true;
|
||||
clickAvatar: function (contributor) {
|
||||
window.location = contributor.html_url;
|
||||
},
|
||||
|
||||
getAvatarUrl: function (contributorData) {
|
||||
return contributorData ? contributorData.avatar_url : '';
|
||||
},
|
||||
|
||||
getContributorsString: function (list, dictionary) {
|
||||
const displayName = (contributorData) => {
|
||||
if (contributorData) {
|
||||
return !contributorData.name
|
||||
? contributorData.login
|
||||
: contributorData.name;
|
||||
}
|
||||
};
|
||||
let contributorString = displayName(dictionary[list[0]]);
|
||||
if (list.length > 2) {
|
||||
contributorString += ` and ${list.length - 1} others`;
|
||||
}
|
||||
if (_.isArray(queryProperty)) {
|
||||
queryProperty = queryProperty.join(', ');
|
||||
if (list.length === 2) {
|
||||
contributorString += ` and ${displayName(dictionary[list[1]])}`;
|
||||
}
|
||||
return _.isString(queryProperty) && queryProperty.toLowerCase().includes(selectedOption.toLowerCase());
|
||||
return contributorString;
|
||||
},
|
||||
|
||||
search: function (library, searchString) {
|
||||
const searchTerms = _.isString(searchString) ? searchString.toLowerCase().split(' ') : [];
|
||||
return library.filter((item) => {
|
||||
const description = _.isString(item.description) ? item.description.toLowerCase() : '';
|
||||
return _.some(searchTerms, (term) => description.includes(term));
|
||||
});
|
||||
},
|
||||
|
||||
setSearchString: function () {
|
||||
this.searchString = this.inputTextValue;
|
||||
},
|
||||
|
||||
delayInput: function(callback, ms, label) {
|
||||
delayInput: function (callback, ms, label) {
|
||||
let inputTimers = this.inputTimers;
|
||||
return function () {
|
||||
label = label || 'defaultTimer';
|
||||
|
|
@ -71,10 +86,85 @@ parasails.registerPage('query-library', {
|
|||
};
|
||||
},
|
||||
|
||||
clickCard: function (querySlug) {
|
||||
window.location = '/queries/' + querySlug;// we can trust the query slug is url-safe
|
||||
setSearchString: function () {
|
||||
this.searchString = this.inputTextValue;
|
||||
},
|
||||
|
||||
}
|
||||
_search: function (library, searchString) {
|
||||
const searchTerms = _.isString(searchString)
|
||||
? searchString.toLowerCase().split(' ')
|
||||
: [];
|
||||
return library.filter((item) => {
|
||||
const description = _.isString(item.description)
|
||||
? item.description.toLowerCase()
|
||||
: '';
|
||||
return searchTerms.some((term) => description.includes(term));
|
||||
});
|
||||
},
|
||||
|
||||
_isIncluded: function (data, selectedOption) {
|
||||
if (selectedOption.startsWith('all') || selectedOption === '') {
|
||||
return true;
|
||||
}
|
||||
if (_.isArray(data)) {
|
||||
data = data.join(', ');
|
||||
}
|
||||
return (
|
||||
_.isString(data) && data.toLowerCase().includes(selectedOption.toLowerCase())
|
||||
);
|
||||
},
|
||||
|
||||
_threadGitHubAPICalls: async function (contributorsList) {
|
||||
// create threads object with a thread for each contributor each thread is a promise that will resolve
|
||||
// when the async call to the GitHub API resolves for that contributor
|
||||
const threads = contributorsList.reduce((threads, contributor) => {
|
||||
threads[contributor] = this._getGitHubUserData(contributor);
|
||||
return threads;
|
||||
}, {});
|
||||
|
||||
// each thread resolves with a key-value pair where the key is the contributor's GitHub handle and the value
|
||||
// is the deserialized JSON response returned by the GitHub API for that contributor
|
||||
const resolvedThreads = await Promise.all(
|
||||
Object.keys(threads).map((key) =>
|
||||
Promise.resolve(threads[key]).then((result) => ({ [key]: result }))
|
||||
)
|
||||
).then((resultsArray) => {
|
||||
const resolvedThreads = resultsArray.reduce(
|
||||
(resolvedThreads, result) => {
|
||||
Object.assign(resolvedThreads, result);
|
||||
return resolvedThreads;
|
||||
},
|
||||
{}
|
||||
);
|
||||
return resolvedThreads;
|
||||
});
|
||||
return resolvedThreads;
|
||||
},
|
||||
|
||||
_getUniqueContributors: function (queries) {
|
||||
return queries.reduce((uniqueContributors, query) => {
|
||||
if (query.contributors) {
|
||||
uniqueContributors = _.union(
|
||||
uniqueContributors,
|
||||
query.contributors.split(',')
|
||||
);
|
||||
}
|
||||
return uniqueContributors;
|
||||
}, []);
|
||||
},
|
||||
|
||||
_getGitHubUserData: async function (gitHubHandle) {
|
||||
const url =
|
||||
'https://api.github.com/users/' + encodeURIComponent(gitHubHandle);
|
||||
const userData = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
},
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.catch(() => {});
|
||||
return userData;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
68
website/assets/styles/pages/query-detail.less
vendored
68
website/assets/styles/pages/query-detail.less
vendored
|
|
@ -6,8 +6,15 @@
|
|||
}
|
||||
|
||||
h6 {
|
||||
font-family: 'Nunito';
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
line-height: 25px;
|
||||
}
|
||||
|
||||
p {
|
||||
&.platform, &.purpose {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
|
|
@ -15,6 +22,13 @@
|
|||
color: @core-vibrant-blue;
|
||||
}
|
||||
|
||||
img {
|
||||
&.logo {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.query-tip {
|
||||
background-color: @ui-off-white;
|
||||
|
||||
|
|
@ -32,4 +46,56 @@
|
|||
|
||||
}
|
||||
|
||||
.avatar-frame {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 50%;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
img {
|
||||
display: inline;
|
||||
margin: 0 auto;
|
||||
height: 100%;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
border-bottom: 1px solid;
|
||||
border-color: #E2E4EA;
|
||||
}
|
||||
|
||||
.platforms, .purpose, .contributors, .contribute {
|
||||
min-height: 36px;
|
||||
p, a {
|
||||
font-family: 'Nunito';
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.remediation {
|
||||
overflow-wrap: break-word;
|
||||
word-wrap: break-word;
|
||||
word-break: break-word;
|
||||
p {
|
||||
font-family: 'Nunito';
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 575px) {
|
||||
|
||||
h2 {
|
||||
font-size: 28px;
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
126
website/assets/styles/pages/query-library.less
vendored
126
website/assets/styles/pages/query-library.less
vendored
|
|
@ -22,18 +22,85 @@
|
|||
color: @core-vibrant-blue;
|
||||
}
|
||||
|
||||
select {
|
||||
color: @core-vibrant-blue;
|
||||
border: none;
|
||||
img {
|
||||
&.logo {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
max-width: 176px;
|
||||
height: 54px;
|
||||
width: 250px;
|
||||
border: 1px solid #C5C7D1;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
&.mobile {
|
||||
width: 100%;
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
color: @core-vibrant-blue;
|
||||
border: 0px;
|
||||
&:focus {
|
||||
border: 0px;
|
||||
}
|
||||
&.select-purpose {
|
||||
width: 102px;
|
||||
}
|
||||
&.select-platform {
|
||||
width: 118px;
|
||||
}
|
||||
&.mobile {
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.library {
|
||||
max-width: 860px;
|
||||
}
|
||||
|
||||
.description {
|
||||
padding: 0px 30px 0px 30px;
|
||||
p {
|
||||
font-size: 16px;
|
||||
line-height: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
.select-mobile-border {
|
||||
height: 54px;
|
||||
width: 100%;
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
border: 1px solid #C5C7D1;
|
||||
border-radius: 8px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.select-mobile {
|
||||
padding-left: 30px;
|
||||
padding-right: 30px;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.search-mobile {
|
||||
padding-left: 30px;
|
||||
padding-right: 30px;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.filter-and-search-bar {
|
||||
margin-left: 30px;
|
||||
margin-right: 30px;
|
||||
padding-left: 45px;
|
||||
padding-right: 45px;
|
||||
}
|
||||
|
||||
.contributors, .platforms {
|
||||
|
|
@ -42,7 +109,6 @@
|
|||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.row {
|
||||
|
|
@ -50,12 +116,17 @@
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
.divider {
|
||||
margin-left: 30px;
|
||||
margin-right: 30px;
|
||||
border-bottom: 1px solid;
|
||||
border-color: #E2E4EA;
|
||||
}
|
||||
|
||||
.card.results {
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
border-bottom: 1px solid;
|
||||
border-color: #E2E4EA;
|
||||
|
||||
&:hover {
|
||||
background-color: #F1F0FF;
|
||||
|
|
@ -69,11 +140,48 @@
|
|||
border-color: @ui-off-white;
|
||||
box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 90px;
|
||||
width: 100%;
|
||||
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 30px;
|
||||
padding-top: 24px;
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
|
||||
.avatar-frame {
|
||||
width: 21px;
|
||||
height: 21px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 50%;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
img {
|
||||
display: inline;
|
||||
margin: 0 auto;
|
||||
height: 100%;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 575px) {
|
||||
|
||||
h2 {
|
||||
font-size: 28px;
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
.contributors, .platforms {
|
||||
|
||||
p {
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
78
website/views/pages/query-detail.ejs
vendored
78
website/views/pages/query-detail.ejs
vendored
|
|
@ -1,9 +1,9 @@
|
|||
<div id="query-detail" v-cloak>
|
||||
|
||||
<div class="d-flex justify-content-center">
|
||||
<div class="d-none d-md-flex justify-content-center">
|
||||
<div class="col-6 my-5">
|
||||
<h2 class="mb-3">{{query.name}}</h2>
|
||||
<h6 class="font-weight-light pb-3">{{query.description}}</h6>
|
||||
<h6 class="pb-3">{{query.description}}</h6>
|
||||
<div v-if="!!query.tip">
|
||||
<div class="container query-tip d-flex align-items-center border-left border-primary p-4 my-5">
|
||||
<img alt="lightbulb" class="lightbulb" src="/images/lightbulb-blue-24x24@2x.png"/><p class="d-flex m-0">{{query.tip}}</p>
|
||||
|
|
@ -11,22 +11,19 @@
|
|||
</div>
|
||||
<h3 class="py-3">Query</h3>
|
||||
<code class="pb-3">{{query.query}}</code>
|
||||
<div v-if="query.purpose === 'Detection' && query.remediation">
|
||||
<div class="remediation" v-if="query.purpose === 'Detection' && query.remediation">
|
||||
<h3 class="pt-5 pb-3">Remediation</h3>
|
||||
<ul class="px-4">
|
||||
<li>{{query.remediation}}</li>
|
||||
</ul>
|
||||
<p>{{query.remediation}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-3 mx-5 my-5 d-none d-md-block">
|
||||
<!-- TODO: refactor as page script to type-check and normalize"-->
|
||||
<div class="query-sidebar border-bottom mb-3">
|
||||
<h5>Platforms</h5>
|
||||
<p>
|
||||
<span v-if="query.platforms.includes('macOS')"><i class="fa fa-apple fa-lg mr-3" alt="Mac"></i></span>
|
||||
<span v-if="query.platforms.includes('Windows')"><i class="fa fa-windows fa-lg mr-3" alt="Windows"></i></span>
|
||||
<span v-if="query.platforms.includes('Linux')"><i class="fa fa-linux fa-lg mr" alt="Linux"></i></span>
|
||||
<span v-if="query.platforms.includes('macOS')"><img class="d-inline mr-3 logo" src="/images/os-macos-black-16x16@2x.png" alt="macOS"/></span>
|
||||
<span v-if="query.platforms.includes('Windows')"><img class="d-inline mr-3 logo" src="/images/os-windows-black-16x16@2x.png" alt="Windows"/></span>
|
||||
<span v-if="query.platforms.includes('Linux')"><img class="d-inline mr-3 logo" src="/images/os-linux-black-16x16@2x.png" alt="Linux"/></span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
@ -35,15 +32,68 @@
|
|||
<p>{{query.purpose}}</p>
|
||||
</div>
|
||||
|
||||
<div class="query-sidebar border-bottom mb-3" v-if="query.contributors && query.contributors.length">
|
||||
<div class="query-sidebar" v-if="query.contributors && query.contributors.length">
|
||||
<h5>Contributors</h5>
|
||||
<!-- TODO: display github avatars"-->
|
||||
<p>{{query.contributors}}</p>
|
||||
<div class="d-flex mb-3">
|
||||
<div v-for="contributor in contributors">
|
||||
<div class="d-flex m-1 avatar-frame" @click="clickAvatar(contributor)">
|
||||
<img alt="GitHub profile image" :alt="contributor.name" :src="contributor.avatar_url"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h5>Contribute to this page</h5>
|
||||
<a target="_blank" :href="'https://github.com/fleetdm/fleet/edit/master/'+queryLibraryYmlRepoPath">View source</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-block d-md-none px-4 py-5 justify-content-center">
|
||||
<h2 class="mb-3">{{query.name}}</h2>
|
||||
<h6 class="pb-3">{{query.description}}</h6>
|
||||
</div>
|
||||
<div class="d-block d-md-none">
|
||||
<div class="col d-sm-flex">
|
||||
<div class="col pr-sm-4 platforms-purpose">
|
||||
<div class="d-flex align-items-center justify-content-between platforms">
|
||||
<h5 class="m-0">Platforms</h5>
|
||||
<p class="m-0 platform">
|
||||
<span v-if="query.platforms.includes('macOS')"><img class="d-inline ml-3 logo" src="/images/os-macos-black-16x16@2x.png" alt="macOS"/></span>
|
||||
<span v-if="query.platforms.includes('Windows')"><img class="d-inline ml-3-3 logo" src="/images/os-windows-black-16x16@2x.png" alt="Windows"/></span>
|
||||
<span v-if="query.platforms.includes('Linux')"><img class="d-inline ml-3 logo" src="/images/os-linux-black-16x16@2x.png" alt="Linux"/></span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="my-3 divider"></div>
|
||||
<div class="d-flex align-items-center justify-content-between purpose">
|
||||
<h5 class="m-0">Purpose</h5>
|
||||
<p class="m-0">{{query.purpose}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-sm-none my-3 divider"></div>
|
||||
<div class="col pl-sm-4 contributors-contribute">
|
||||
<div class="d-flex align-items-center justify-content-between contributors">
|
||||
<h5 class="m-0">Contributors</h5>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="d-flex" v-for="contributor in contributors">
|
||||
<div class="d-flex m-1 avatar-frame" @click="clickAvatar(contributor)">
|
||||
<img alt="GitHub profile image" :alt="contributor.name" :src="contributor.avatar_url"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="my-3 divider"></div>
|
||||
<div class="d-flex align-items-center justify-content-between contribute">
|
||||
<h5 class="m-0">Contribute to this page</h5>
|
||||
<a class="text-right m-0" target="_blank" :href="'https://github.com/fleetdm/fleet/edit/master/'+queryLibraryYmlRepoPath">View source</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-block d-md-none py-5 justify-content-center" style="padding-left: 30px; padding-right: 30px;">
|
||||
<h3 class="py-3">Query</h3>
|
||||
<code class="pb-3">{{query.query}}</code>
|
||||
<div class="remediation" v-if="query.purpose === 'Detection'">
|
||||
<h3 class="pt-5 pb-3">Remediation</h3>
|
||||
<p>{{!query.remediation ? "N/A" : query.remediation}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<%- /* Expose server-rendered data as window.SAILS_LOCALS :: */ exposeLocalsToBrowser() %>
|
||||
|
|
|
|||
79
website/views/pages/query-library.ejs
vendored
79
website/views/pages/query-library.ejs
vendored
|
|
@ -1,24 +1,49 @@
|
|||
<div id="query-library" v-cloak>
|
||||
|
||||
<div class="d-flex justify-content-center">
|
||||
<div class="col-8 my-5">
|
||||
<div class="col-6-grow justify-content-center my-5 library">
|
||||
<h2 class="mb-3">Standard query library</h2>
|
||||
<h6 class="font-weight-light pb-3">Fleet's standard query library includes a growing collection of useful queries for organizations deploying Fleet and osquery.</h6>
|
||||
<div class="filter-and-search-bar container">
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-8 d-flex px-0">
|
||||
<p class="pb-3 description">Fleet's standard query library includes a growing collection of useful queries for organizations deploying Fleet and osquery.</p>
|
||||
<div class="p-0 m-0">
|
||||
<div class="d-sm-none">
|
||||
<div class="d-flex search-mobile">
|
||||
<input class="mobile" v-model="inputTextValue" placeholder="Search queries" @keydown.self="delayInput(setSearchString, 500, 'defaultTimer')()"/>
|
||||
</div>
|
||||
<div class="d-flex select-mobile">
|
||||
<div class="select-mobile-border">
|
||||
<select class="select-purpose mobile font-weight-bold" v-model="selectedPurpose">
|
||||
<option value="all" selected>All queries</option>
|
||||
<option value="information">Informational queries</option>
|
||||
<option value="detection">Detection queries</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex select-mobile">
|
||||
<div class="select-mobile-border">
|
||||
<select class="select-purpose mobile font-weight-bold" v-model="selectedPlatform">
|
||||
<option value="all" selected>All platforms</option>
|
||||
<option value="macOS">macOS</option>
|
||||
<option value="Windows">Windows</option>
|
||||
<option value="Linux">Linux</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filter-and-search-bar d-none d-sm-flex row justify-content-between">
|
||||
<div class="d-flex col col-xs-7 px-0">
|
||||
<div class="filter-purpose">
|
||||
<span>Show
|
||||
<select class="mr-1" v-model="selectedPurpose">
|
||||
<select class="mr-1 select-purpose" v-model="selectedPurpose">
|
||||
<option value="all" selected>all queries</option>
|
||||
<option value="information">informational</option>
|
||||
<option value="information">information</option>
|
||||
<option value="detection">detection</option>
|
||||
</select>
|
||||
</span>
|
||||
</div>
|
||||
<div class="filter-platform">
|
||||
<span> compatible with
|
||||
<select class="mr-1" v-model="selectedPlatform">
|
||||
<select class="mr-1 select-platform" v-model="selectedPlatform">
|
||||
<option value="all" selected>all platforms</option>
|
||||
<option value="macOS">macOS</option>
|
||||
<option value="Windows">Windows</option>
|
||||
|
|
@ -27,9 +52,9 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-3 px-0 d-none d-lg-block justify-content-end">
|
||||
<div class="search ">
|
||||
<input v-model="inputTextValue" placeholder="Search" @keydown.self="delayInput(setSearchString, 1000, 'defaultTimer')()"/>
|
||||
<div class="col col-xs-5 px-0 justify-content-end">
|
||||
<div class="search d-flex justify-content-end">
|
||||
<input v-model="inputTextValue" placeholder="Search queries" @keydown.self="delayInput(setSearchString, 500, 'defaultTimer')()"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -38,33 +63,41 @@
|
|||
<div class="category__informational">
|
||||
<div v-for="query of queriesList">
|
||||
<div class="card results" @click="clickCard(query.slug)">
|
||||
<div class="card-body">
|
||||
<div class="card-body query-card">
|
||||
<div class="row justify-content-between align-items-center">
|
||||
<div class="col-10">
|
||||
<div class="col-sm-9 col-md-10">
|
||||
<h5 class="card-title m-0">{{query.name}}</h5>
|
||||
<h6 class="font-italic mb-1 p-0">{{query.description}}</h6>
|
||||
<p class="font-italic mb-1 p-0 description">{{query.description}}</p>
|
||||
<div class="contributors" v-if="query.contributors && query.contributors.length">
|
||||
<p class="mb-0">contributed by {{query.contributors}}</p>
|
||||
<div class="d-flex mb-2 mb-sm-1 align-items-center">
|
||||
<div v-for="contributor in query.contributors.split(',')">
|
||||
<div class="d-flex m-1 avatar-frame" @click="clickAvatar(contributor)">
|
||||
<img alt="GitHub profile image" :alt="contributor" :src="getAvatarUrl(contributorsDictionary[contributor])"/>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mb-0 ml-1">contributed by {{getContributorsString(query.contributors.split(','), contributorsDictionary)}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<div class="text-right m-0">
|
||||
<span v-if="query.platforms.includes('macOS')"><i class="fa fa-apple fa-md ml-1" alt="Mac"></i></span>
|
||||
<span v-if="query.platforms.includes('Windows')"><i class="fa fa-windows fa-md ml-1" alt="Windows"></i></span>
|
||||
<span v-if="query.platforms.includes('Linux')"><i class="fa fa-linux fa-md ml-1" alt="Linux"></i></span>
|
||||
<div class="col-sm-3 col-md-2">
|
||||
<div class="text-sm-right m-0">
|
||||
<span v-if="query.platforms.includes('macOS')"><img class="d-inline mr-1 mr-sm-0 ml-sm-1 ml-md-2 logo" src="/images/os-macos-black-16x16@2x.png" alt="macOS"/></span>
|
||||
<span v-if="query.platforms.includes('Windows')"><img class="d-inline mr-1 mr-sm-0 ml-sm-1 ml-md-2 logo" src="/images/os-windows-black-16x16@2x.png" alt="Windows"/></span>
|
||||
<span v-if="query.platforms.includes('Linux')"><img class="d-inline mr-1 mr-ms-0 ml-sm-1 ml-md-2 logo" src="/images/os-linux-black-16x16@2x.png" alt="Linux"/></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-center">
|
||||
<div class="card call-to-action col-8">
|
||||
<div class="card-body flex-fill">
|
||||
<div class="d-flex justify-content-center p-3">
|
||||
<div class="card call-to-action col-6-grow my-5 library">
|
||||
<div class="card-body">
|
||||
<h3 class="mb-3">Contributors</h3>
|
||||
<p><strong>Want to add your own query?</strong> Please submit a pull request <a href="https://github.com/fleetdm/fleet/tree/master/handbook/queries" >over on GitHub</a>.</p>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue