mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 08:55:24 +00:00
Query library improvements -- Round 2 (#973)
-Modify build-static-content back-end script to implement GitHub Users API and build contributor profile information into query library pages -Remove related functionality from client-side page scripts -Add dropdown menu to select filters -Refine html and css
This commit is contained in:
parent
fccc58536b
commit
676f3f0bd5
@ -17,6 +17,7 @@ spec:
|
||||
description: Retrieves the OpenSSL version.
|
||||
query: SELECT name AS name, version AS version, 'deb_packages' AS source FROM deb_packages WHERE name LIKE 'openssl%' UNION SELECT name AS name, version AS version, 'apt_sources' AS source FROM apt_sources WHERE name LIKE 'openssl%' UNION SELECT name AS name, version AS version, 'rpm_packages' AS source FROM rpm_packages WHERE name LIKE 'openssl%';
|
||||
purpose: Detection
|
||||
contributors: zwass
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: query
|
||||
@ -26,6 +27,7 @@ spec:
|
||||
description: Gatekeeper tries to ensure only trusted software is run on a mac machine.
|
||||
query: SELECT * FROM gatekeeper WHERE assessments_enabled = 0;
|
||||
purpose: Detection
|
||||
contributors: zwass
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: query
|
||||
@ -36,6 +38,7 @@ spec:
|
||||
query: SELECT username, authorized_keys. * FROM users CROSS JOIN authorized_keys USING (uid);
|
||||
purpose: Detection
|
||||
remediation: Check out the linked table (https://github.com/fleetdm/fleet/blob/32b4d53e7f1428ce43b0f9fa52838cbe7b413eed/handbook/queries/detect-hosts-with-high-severity-vulnerable-versions-of-openssl.md#table-of-vulnerable-openssl-versions) to determine if the installed version is a high severity vulnerability and view the corresponding CVE(s)
|
||||
contributors: mike-j-thomas
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: query
|
||||
@ -65,6 +68,7 @@ spec:
|
||||
description: Retrieve application, system, and mobile app crash logs.
|
||||
query: SELECT uid, datetime, responsible, exception_type, identifier, version, crash_path FROM users CROSS JOIN crashes USING (uid);
|
||||
purpose: Informational
|
||||
contributors: zwass
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: query
|
||||
@ -74,6 +78,7 @@ spec:
|
||||
description: List installed Chrome Extensions for all users.
|
||||
query: SELECT uid, datetime, responsible, exception_type, identifier, version, crash_path FROM users CROSS JOIN crashes USING (uid);
|
||||
purpose: Informational
|
||||
contributors: zwass
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: query
|
||||
@ -83,6 +88,7 @@ spec:
|
||||
description: Get all software installed on a FreeBSD computer, including browser plugins and installed packages. Note, this does not included other running processes in the processes table.
|
||||
query: SELECT name AS name, version AS version, 'Browser plugin (Chrome)' AS type, 'chrome_extensions' AS source FROM chrome_extensions UNION SELECT name AS name, version AS version, 'Browser plugin (Firefox)' AS type, 'firefox_addons' AS source FROM firefox_addons UNION SELECT name AS name, version AS version, 'Package (Atom)' AS type, 'atom_packages' AS source FROM atom_packages UNION SELECT name AS name, version AS version, 'Package (Python)' AS type, 'python_packages' AS source FROM python_packages UNION SELECT name AS name, version AS version, 'Package (pkg)' AS type, 'pkg_packages' AS source FROM pkg_packages;
|
||||
purpose: Informational
|
||||
contributors: zwass
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: query
|
||||
@ -92,6 +98,7 @@ spec:
|
||||
description: Get the installed homebrew package database.
|
||||
query: SELECT * FROM homebrew_packages;
|
||||
purpose: Informational
|
||||
contributors: zwass
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: query
|
||||
@ -101,6 +108,7 @@ spec:
|
||||
description: Get all software installed on a Linux computer, including browser plugins and installed packages. Note, this does not included other running processes in the processes table.
|
||||
query: SELECT name AS name, version AS version, 'Package (APT)' AS type, 'apt_sources' AS source FROM apt_sources UNION SELECT name AS name, version AS version, 'Package (deb)' AS type, 'deb_packages' AS source FROM deb_packages UNION SELECT package AS name, version AS version, 'Package (Portage)' AS type, 'portage_packages' AS source FROM portage_packages UNION SELECT name AS name, version AS version, 'Package (RPM)' AS type, 'rpm_packages' AS source FROM rpm_packages UNION SELECT name AS name, '' AS version, 'Package (YUM)' AS type, 'yum_sources' AS source FROM yum_sources UNION SELECT name AS name, version AS version, 'Package (NPM)' AS type, 'npm_packages' AS source FROM npm_packages UNION SELECT name AS name, version AS version, 'Package (Atom)' AS type, 'atom_packages' AS source FROM atom_packages UNION SELECT name AS name, version AS version, 'Package (Python)' AS type, 'python_packages' AS source FROM python_packages;
|
||||
purpose: Informational
|
||||
contributors: zwass
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: query
|
||||
@ -110,6 +118,7 @@ spec:
|
||||
description: Get all software installed on a macOS computer, including apps, browser plugins, and installed packages. Note, this does not included other running processes in the processes table.
|
||||
query: SELECT name AS name, bundle_short_version AS version, 'Application (macOS)' AS type, 'apps' AS source FROM apps UNION SELECT name AS name, version AS version, 'Package (Python)' AS type, 'python_packages' AS source FROM python_packages UNION SELECT name AS name, version AS version, 'Browser plugin (Chrome)' AS type, 'chrome_extensions' AS source FROM chrome_extensions UNION SELECT name AS name, version AS version, 'Browser plugin (Firefox)' AS type, 'firefox_addons' AS source FROM firefox_addons UNION SELECT name As name, version AS version, 'Browser plugin (Safari)' AS type, 'safari_extensions' AS source FROM safari_extensions UNION SELECT name AS name, version AS version, 'Package (Homebrew)' AS type, 'homebrew_packages' AS source FROM homebrew_packages;
|
||||
purpose: Informational
|
||||
contributors: zwass
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: query
|
||||
@ -119,6 +128,7 @@ spec:
|
||||
description: Retrieves the list of installed Safari Extensions for all users in the target system.
|
||||
query: SELECT safari_extensions.* FROM users join safari_extensions USING (uid);
|
||||
purpose: Informational
|
||||
contributors: zwass
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: query
|
||||
@ -128,6 +138,7 @@ spec:
|
||||
description: Get all software installed on a Windows computer, including programs, browser plugins, and installed packages. Note, this does not included other running processes in the processes table.
|
||||
query: SELECT name AS name, version AS version, 'Program (Windows)' AS type, 'programs' AS source FROM programs UNION SELECT name AS name, version AS version, 'Package (Python)' AS type, 'python_packages' AS source FROM python_packages UNION SELECT name AS name, version AS version, 'Browser plugin (IE)' AS type, 'ie_extensions' AS source FROM ie_extensions UNION SELECT name AS name, version AS version, 'Browser plugin (Chrome)' AS type, 'chrome_extensions' AS source FROM chrome_extensions UNION SELECT name AS name, version AS version, 'Browser plugin (Firefox)' AS type, 'firefox_addons' AS source FROM firefox_addons UNION SELECT name AS name, version AS version, 'Package (Chocolatey)' AS type, 'chocolatey_packages' AS source FROM chocolatey_packages UNION SELECT name AS name, version AS version, 'Package (Atom)' AS type, 'atom_packages' AS source FROM atom_packages UNION SELECT name AS name, version AS version, 'Package (Python)' AS type, 'python_packages' AS source FROM python_packages;
|
||||
purpose: Informational
|
||||
contributors: zwass
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: query
|
||||
@ -137,6 +148,7 @@ spec:
|
||||
description:
|
||||
query: SELECT * FROM battery WHERE health != 'Good' AND condition NOT IN ('', 'Normal');
|
||||
purpose: Informational
|
||||
contributors: zwass
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: query
|
||||
@ -146,6 +158,7 @@ spec:
|
||||
description: Displays the percentage of free space available on the primary disk partition.
|
||||
query: SELECT (blocks_available * 100 / blocks) AS pct, * FROM mounts WHERE path = '/';
|
||||
purpose: Informational
|
||||
contributors: zwass
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: query
|
||||
@ -155,6 +168,7 @@ spec:
|
||||
description: Shows system mounted devices and filesystems (not process specific).
|
||||
query: SELECT device, device_alias, path, type, blocks_size FROM mounts;
|
||||
purpose: Informational
|
||||
contributors: zwass
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: query
|
||||
@ -164,6 +178,7 @@ spec:
|
||||
description: Shows system mounted devices and filesystems (not process specific).
|
||||
query: SELECT * FROM os_version;
|
||||
purpose: Informational
|
||||
contributors: zwass
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: query
|
||||
@ -173,6 +188,7 @@ spec:
|
||||
description: Shows information about the host platform
|
||||
query: SELECT vendor, version, date, revision from platform_info;
|
||||
purpose: Informational
|
||||
contributors: zwass
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: query
|
||||
@ -182,6 +198,7 @@ spec:
|
||||
description: Shows applications and binaries set as user/login startup items.
|
||||
query: SELECT * FROM startup_items;
|
||||
purpose: Informational
|
||||
contributors: zwass
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: query
|
||||
@ -191,6 +208,7 @@ spec:
|
||||
description: Get a list of system logins and logouts.
|
||||
query: SELECT * FROM last;
|
||||
purpose: Informational
|
||||
contributors: zwass
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: query
|
||||
@ -210,6 +228,7 @@ spec:
|
||||
description: Shows the system uptime.
|
||||
query: SELECT * FROM uptime;
|
||||
purpose: Informational
|
||||
contributors: zwass
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: query
|
||||
@ -219,6 +238,7 @@ spec:
|
||||
description: Shows all USB devices that are actively plugged into the host system.
|
||||
query: SELECT * FROM usb_devices;
|
||||
purpose: Informational
|
||||
contributors: zwass
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: query
|
||||
@ -228,6 +248,7 @@ spec:
|
||||
description: Shows information about the wifi network that a host is currently connected to.
|
||||
query: SELECT * FROM wifi_status;
|
||||
purpose: Informational
|
||||
contributors: zwass
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: query
|
||||
@ -237,6 +258,7 @@ spec:
|
||||
description:
|
||||
query: SELECT * FROM bitlocker_info WHERE protection_status = 0;
|
||||
purpose: Informational
|
||||
contributors: zwass
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: query
|
||||
@ -288,6 +310,7 @@ spec:
|
||||
description: suid binaries in common locations.
|
||||
query: SELECT * FROM suid_bin;
|
||||
purpose: Informational
|
||||
contributors: zwass
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: query
|
||||
|
BIN
website/assets/images/icon-search-16x16@2x.png
vendored
BIN
website/assets/images/icon-search-16x16@2x.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 814 B After Width: | Height: | Size: 798 B |
24
website/assets/js/pages/query-detail.page.js
vendored
24
website/assets/js/pages/query-detail.page.js
vendored
@ -13,35 +13,19 @@ parasails.registerPage('query-detail', {
|
||||
//…
|
||||
},
|
||||
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;
|
||||
window.location = contributor.htmlUrl;
|
||||
},
|
||||
getDisplayName: function (contributor) {
|
||||
return !contributor.name ? contributor.handle : contributor.name;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
118
website/assets/js/pages/query-library.page.js
vendored
118
website/assets/js/pages/query-library.page.js
vendored
@ -3,12 +3,11 @@ parasails.registerPage('query-library', {
|
||||
// ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣
|
||||
// ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝
|
||||
data: {
|
||||
contributorsDictionary: {},
|
||||
inputTextValue: '',
|
||||
inputTimers: {},
|
||||
searchString: '', // The user input string to be searched against the query library
|
||||
selectedPurpose: 'all', // Initially set to all, the user may select a different option to filter queries by purpose (e.g., "all queries", "information", "detection")
|
||||
selectedPlatform: 'all', // Initially set to all, the user may select a different option to filter queries by platform (e.g., "all platforms", "macOS", "Windows", "Linux")
|
||||
selectedPurpose: 'all queries', // Initially set to all, the user may select a different option to filter queries by purpose (e.g., "all queries", "information", "detection")
|
||||
selectedPlatform: 'all platforms', // Initially set to all, the user may select a different option to filter queries by platform (e.g., "all platforms", "macOS", "Windows", "Linux")
|
||||
},
|
||||
|
||||
computed: {
|
||||
@ -36,43 +35,50 @@ parasails.registerPage('query-library', {
|
||||
//…
|
||||
},
|
||||
mounted: async function () {
|
||||
const uniqueContributors = this._getUniqueContributors(this.queries);
|
||||
this.contributorsDictionary = Object.assign(
|
||||
{},
|
||||
await this._threadGitHubAPICalls(uniqueContributors)
|
||||
);
|
||||
//…
|
||||
},
|
||||
|
||||
// ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
|
||||
// ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗
|
||||
// ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝
|
||||
methods: {
|
||||
clickSelectPurpose(purpose) {
|
||||
this.selectedPurpose = purpose;
|
||||
},
|
||||
|
||||
clickSelectPlatform(platform) {
|
||||
this.selectedPlatform = platform;
|
||||
},
|
||||
|
||||
clickCard: function (querySlug) {
|
||||
window.location = '/queries/' + querySlug; // we can trust the query slug is url-safe
|
||||
},
|
||||
|
||||
clickAvatar: function (contributor) {
|
||||
window.location = contributor.html_url;
|
||||
window.location = contributor.htmlUrl;
|
||||
},
|
||||
|
||||
getAvatarUrl: function (contributorData) {
|
||||
return contributorData ? contributorData.avatar_url : '';
|
||||
return contributorData ? contributorData.avatarUrl : '';
|
||||
},
|
||||
|
||||
getContributorsString: function (list, dictionary) {
|
||||
getContributorsString: function (contributors) {
|
||||
if (!contributors) {
|
||||
return;
|
||||
}
|
||||
const displayName = (contributorData) => {
|
||||
if (contributorData) {
|
||||
return !contributorData.name
|
||||
? contributorData.login
|
||||
? contributorData.handle
|
||||
: contributorData.name;
|
||||
}
|
||||
};
|
||||
let contributorString = displayName(dictionary[list[0]]);
|
||||
if (list.length > 2) {
|
||||
contributorString += ` and ${list.length - 1} others`;
|
||||
let contributorString = displayName(contributors[0]);
|
||||
if (contributors.length > 2) {
|
||||
contributorString += ` and ${contributors.length - 1} others`;
|
||||
}
|
||||
if (list.length === 2) {
|
||||
contributorString += ` and ${displayName(dictionary[list[1]])}`;
|
||||
if (contributors.length === 2) {
|
||||
contributorString += ` and ${displayName(contributors[1])}`;
|
||||
}
|
||||
return contributorString;
|
||||
},
|
||||
@ -90,15 +96,22 @@ parasails.registerPage('query-library', {
|
||||
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));
|
||||
_search: function (queries, searchString) {
|
||||
if (_.isEmpty(searchString)) {
|
||||
return queries;
|
||||
}
|
||||
|
||||
const normalize = (value) => _.isString(value) ? value.toLowerCase() : '';
|
||||
const searchTerms = normalize(searchString).split(' ');
|
||||
|
||||
return queries.filter((query) => {
|
||||
let textToSearch = normalize(query.name) + ', ' + normalize(query.description);
|
||||
if (query.contributors) {
|
||||
query.contributors.forEach((contributor) => {
|
||||
textToSearch += ', ' + normalize(contributor.name) + ', ' + normalize(contributor.handle);
|
||||
});
|
||||
}
|
||||
return (searchTerms.some((term) => textToSearch.includes(term)));
|
||||
});
|
||||
},
|
||||
|
||||
@ -114,57 +127,6 @@ parasails.registerPage('query-library', {
|
||||
);
|
||||
},
|
||||
|
||||
_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;
|
||||
},
|
||||
},
|
||||
|
||||
});
|
||||
|
@ -125,9 +125,6 @@ a.text-danger:hover, a.text-danger:focus {
|
||||
box-shadow: 0px 6px 20px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.dropdown:hover > .dropdown-menu {
|
||||
display: block;
|
||||
}
|
||||
.dropdown:hover > .btn {
|
||||
color: #6a67fe;
|
||||
}
|
||||
|
9
website/assets/styles/layout.less
vendored
9
website/assets/styles/layout.less
vendored
@ -20,15 +20,15 @@ html, body {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
.dropdown:hover > .dropdown-menu {
|
||||
display: block;
|
||||
}
|
||||
.header-btn {
|
||||
color: #ffffff;
|
||||
cursor: unset;
|
||||
font-family: @navigation-font;
|
||||
font-weight: @bold;
|
||||
}
|
||||
// .header-btn[aria-expanded='true'] {
|
||||
// color: #6a67fe;
|
||||
// }
|
||||
.header-link {
|
||||
color: #ffffff;
|
||||
font-weight: @bold;
|
||||
@ -104,6 +104,9 @@ html, body {
|
||||
left: 0;
|
||||
right: 0;
|
||||
border-bottom: 1px solid #e2e4ea;
|
||||
.dropdown:hover > .dropdown-menu {
|
||||
display: block;
|
||||
}
|
||||
.header-btn {
|
||||
color: #192147;
|
||||
cursor: unset;
|
||||
|
15
website/assets/styles/pages/query-detail.less
vendored
15
website/assets/styles/pages/query-detail.less
vendored
@ -71,10 +71,10 @@
|
||||
|
||||
.platforms, .purpose, .contributors, .contribute {
|
||||
min-height: 36px;
|
||||
line-height: 24px;
|
||||
p, a {
|
||||
font-family: 'Nunito';
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,6 +89,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
|
||||
.contribute {
|
||||
flex-wrap: wrap;
|
||||
min-height: 24px;
|
||||
h5 {
|
||||
padding: 6px 0px 6px 0px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@media (max-width: 575px) {
|
||||
|
||||
h2 {
|
||||
|
111
website/assets/styles/pages/query-library.less
vendored
111
website/assets/styles/pages/query-library.less
vendored
@ -1,5 +1,4 @@
|
||||
#query-library {
|
||||
|
||||
h2 {
|
||||
padding: 0px 30px 0px 30px;
|
||||
}
|
||||
@ -14,7 +13,6 @@
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
padding: 0px 30px 0px 30px;
|
||||
|
||||
}
|
||||
|
||||
a {
|
||||
@ -27,24 +25,83 @@
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
&.search {
|
||||
transform: scale(0.5);
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
height: 54px;
|
||||
&::placeholder {
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.input-group {
|
||||
&.search {
|
||||
width: 250px;
|
||||
border: 1px solid #C5C7D1;
|
||||
}
|
||||
}
|
||||
|
||||
.input-group-text {
|
||||
color: #8b8fa2;
|
||||
border-color: #c5c7d1;
|
||||
border-top-left-radius: 8px;
|
||||
border-bottom-left-radius: 8px;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
font-size: 16px;
|
||||
border-color: #c5c7d1;
|
||||
border-top-right-radius: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
&:focus {
|
||||
border: 1px solid #c5c7d1;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus, .show > .btn-secondary.dropdown-toggle:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
font-family: Nunito;
|
||||
color: @core-vibrant-blue;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
&:focus {
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.filters {
|
||||
height: 54px;
|
||||
p {
|
||||
font-family: Nunito;
|
||||
font-size: 16px;
|
||||
line-height: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
&.mobile {
|
||||
width: 100%;
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
.dropdown-item {
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: lightness(@core-vibrant-blue, 10%);
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
color: @core-vibrant-blue;
|
||||
border: 0px;
|
||||
outline: 0;
|
||||
&:focus {
|
||||
border: 0px;
|
||||
}
|
||||
@ -65,7 +122,9 @@
|
||||
}
|
||||
|
||||
.library {
|
||||
max-width: 860px;
|
||||
max-width: 960px;
|
||||
margin-top: 80px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.description {
|
||||
@ -81,7 +140,7 @@
|
||||
width: 100%;
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
border: 1px solid #C5C7D1;
|
||||
border: 1px solid #c5c7d1;
|
||||
border-radius: 8px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
@ -101,10 +160,11 @@
|
||||
.filter-and-search-bar {
|
||||
padding-left: 45px;
|
||||
padding-right: 45px;
|
||||
margin-bottom: 0;
|
||||
min-height: 54px;
|
||||
}
|
||||
|
||||
.contributors, .platforms {
|
||||
|
||||
p {
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
@ -120,7 +180,7 @@
|
||||
margin-left: 30px;
|
||||
margin-right: 30px;
|
||||
border-bottom: 1px solid;
|
||||
border-color: #E2E4EA;
|
||||
border-color: #e2e4ea;
|
||||
}
|
||||
|
||||
.card.results {
|
||||
@ -129,7 +189,7 @@
|
||||
border-radius: 8px;
|
||||
|
||||
&:hover {
|
||||
background-color: #F1F0FF;
|
||||
background-color: #f1f0ff;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
@ -141,7 +201,6 @@
|
||||
box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 90px;
|
||||
width: 100%;
|
||||
|
||||
}
|
||||
|
||||
.card-body {
|
||||
@ -168,20 +227,36 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 575px) {
|
||||
@media (max-width: 768px) {
|
||||
.library {
|
||||
max-width: 720px;
|
||||
margin-top: 60px;
|
||||
}
|
||||
.results {
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 575px) {
|
||||
h2 {
|
||||
font-size: 28px;
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
.contributors, .platforms {
|
||||
.library {
|
||||
max-width: none;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.results {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.contributors, .platforms {
|
||||
p {
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
42
website/scripts/build-static-content.js
vendored
42
website/scripts/build-static-content.js
vendored
@ -29,6 +29,7 @@ module.exports = {
|
||||
let yaml = await sails.helpers.fs.read(path.join(topLvlRepoPath, RELATIVE_PATH_TO_QUERY_LIBRARY_YML_IN_FLEET_REPO));
|
||||
|
||||
let queriesWithProblematicRemediations = [];
|
||||
let queriesWithProblematicContributors = [];
|
||||
let queries = YAML.parseAllDocuments(yaml).map((yamlDocument)=>{
|
||||
let query = yamlDocument.toJSON().spec;
|
||||
query.slug = _.kebabCase(query.name);// « unique slug to use for routing to this query's detail page
|
||||
@ -38,6 +39,12 @@ module.exports = {
|
||||
} else if (query.remediation === undefined) {
|
||||
query.remediation = 'N/A';// « We set this to a string here so that the data type is always string. We use N/A so folks can see there's no remediation and contribute if desired.
|
||||
}
|
||||
|
||||
// GitHub usernames may only contain alphanumeric characters or single hyphens, and cannot begin or end with a hyphen.
|
||||
if (!query.contributors || (query.contributors !== undefined && !_.isString(query.contributors)) || query.contributors.split(',').some((contributor) => contributor.match('^[^A-za-z0-9].*|[^A-Za-z0-9-]|.*[^A-za-z0-9]$'))) {
|
||||
queriesWithProblematicContributors.push(query);
|
||||
}
|
||||
|
||||
return query;
|
||||
});
|
||||
// Report any errors that were detected along the way in one fell swoop to avoid endless resubmitting of PRs.
|
||||
@ -48,6 +55,41 @@ module.exports = {
|
||||
if (queries.length !== _.uniq(_.pluck(queries, 'slug')).length) {
|
||||
throw new Error('Failed parsing YAML for query library: Queries as currently named would result in colliding (duplicate) slugs. To resolve, rename the queries whose names are too similar. Note the duplicates: ' + _.pluck(queries, 'slug').sort());
|
||||
}//•
|
||||
// Report any errors that were detected along the way in one fell swoop to avoid endless resubmitting of PRs.
|
||||
if (queriesWithProblematicContributors.length >= 1) {
|
||||
throw new Error('Failed parsing YAML for query library: The "contributors" of a query should be a single string of valid GitHub user names (e.g. "zwass", or "zwass,noahtalerman,mikermcneil"). But one or more queries have an invalid "contributors" value: ' + _.pluck(queriesWithProblematicContributors, 'slug').sort());
|
||||
}//•
|
||||
|
||||
// Get a distinct list of all GitHub usernames from all of our queries.
|
||||
// Map all queries to build a list of unique contributor names then build a dictionary of user profile information from the GitHub Users API
|
||||
const githubUsernames = queries.reduce((list, query) => {
|
||||
if (!queriesWithProblematicContributors.find((element) => element.slug === query.slug)) {
|
||||
list = _.union(list, query.contributors.split(','));
|
||||
}
|
||||
return list;
|
||||
}, []);
|
||||
|
||||
// Talk to GitHub and get additional information about each contributor.
|
||||
let githubDataByUsername = {};
|
||||
await sails.helpers.flow.simultaneouslyForEach(githubUsernames, async(username)=>{
|
||||
githubDataByUsername[username] = await sails.helpers.http.get('https://api.github.com/users/' + encodeURIComponent(username), {}, { 'User-Agent': 'Fleet-Standard-Query-Library', Accept: 'application/vnd.github.v3+json' });
|
||||
});//∞
|
||||
|
||||
// Now expand queries with relevant profile data for the contributors.
|
||||
for (let query of queries) {
|
||||
let usernames = query.contributors.split(',');
|
||||
let contributorProfiles = [];
|
||||
for (let username of usernames) {
|
||||
contributorProfiles.push({
|
||||
name: githubDataByUsername[username].name,
|
||||
handle: githubDataByUsername[username].login,
|
||||
avatarUrl: githubDataByUsername[username].avatar_url,
|
||||
htmlUrl: githubDataByUsername[username].html_url,
|
||||
});
|
||||
}
|
||||
query.contributors = contributorProfiles;
|
||||
}
|
||||
|
||||
// Attach to Sails app configuration.
|
||||
builtStaticContent.queries = queries;
|
||||
builtStaticContent.queryLibraryYmlRepoPath = RELATIVE_PATH_TO_QUERY_LIBRARY_YML_IN_FLEET_REPO;
|
||||
|
81
website/views/pages/query-detail.ejs
vendored
81
website/views/pages/query-detail.ejs
vendored
@ -1,43 +1,48 @@
|
||||
<div id="query-detail" v-cloak>
|
||||
|
||||
<div class="d-none d-md-flex justify-content-center">
|
||||
<div class="col-6 my-5">
|
||||
<div class="d-none d-md-flex p-md-4 justify-content-center">
|
||||
<div class="d-flex container-xl justify-content-center">
|
||||
<div class="d-flex container-fluid">
|
||||
<div class="col-8 pl-md-4 my-5">
|
||||
<h2 class="mb-3">{{query.name}}</h2>
|
||||
<h6 class="pb-3">{{query.description}}</h6>
|
||||
<h6 class="pb-3">{{query.description ? 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>
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="py-3">Query</h3>
|
||||
<code class="pb-3">{{query.query}}</code>
|
||||
<code class="pb-3">{{query.query ? query.query : '--'}}</code>
|
||||
<div class="remediation" v-if="query.purpose === 'Detection' && query.remediation">
|
||||
<h3 class="pt-5 pb-3">Remediation</h3>
|
||||
<p>{{query.remediation}}</p>
|
||||
<p>{{query.remediation ? query.remediation : 'N/A'}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-3 mx-5 my-5 d-none d-md-block">
|
||||
<div class="col-4 pr-md-4 ml-5 my-5">
|
||||
<div class="query-sidebar border-bottom mb-3">
|
||||
<h5>Platforms</h5>
|
||||
<p>
|
||||
<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>
|
||||
<p v-if="!query.platforms || !query.platforms.length">--</p>
|
||||
<div class="d-flex mb-3" v-else>
|
||||
<img class="d-inline-flex mr-3 logo" src="/images/os-macos-black-24x24@2x.png" alt="macOS" v-if="query.platforms.includes('macOS')"/>
|
||||
<img class="d-inline-flex mr-3 logo" src="/images/os-windows-black-24x24@2x.png" alt="Windows" v-if="query.platforms.includes('Windows')"/>
|
||||
<img class="d-inline-flex mr-3 logo" src="/images/os-linux-black-24x24@2x.png" alt="Linux" v-if="query.platforms.includes('Linux')"/>
|
||||
<img class="d-inline-flex mr-3 logo" src="/images/os-freebsd-black-24x24@2x.png" alt="FreeBSD" v-if="query.platforms.includes('FreeBSD')"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="query-sidebar border-bottom mb-3">
|
||||
<h5>Purpose</h5>
|
||||
<p>{{query.purpose}}</p>
|
||||
<p>{{query.purpose ? query.purpose : '--'}}</p>
|
||||
</div>
|
||||
|
||||
<div class="query-sidebar" v-if="query.contributors && query.contributors.length">
|
||||
<div class="query-sidebar">
|
||||
<h5>Contributors</h5>
|
||||
<div class="d-flex mb-3">
|
||||
<div v-for="contributor in contributors">
|
||||
<span class="mb-1" v-if="!query.contributors || !query.contributors.length">--</span>
|
||||
<div v-for="contributor in query.contributors">
|
||||
<div class="d-flex m-1 avatar-frame" @click="clickAvatar(contributor)">
|
||||
<img alt="GitHub profile image" :alt="contributor.name" :src="contributor.avatar_url"/>
|
||||
<img alt="a GitHub user avatar" :alt="contributor.name ? contributor.name : contributor.handle" :src="contributor.avatarUrl"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -46,53 +51,59 @@
|
||||
<a target="_blank" :href="'https://github.com/fleetdm/fleet/edit/main/'+queryLibraryYmlRepoPath">View source</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-block d-md-none px-4 py-5 justify-content-center">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-block d-md-none px-3 py-5 justify-content-center">
|
||||
<h2 class="mb-3">{{query.name}}</h2>
|
||||
<h6 class="pb-3">{{query.description}}</h6>
|
||||
<h6 class="pb-0 mb-0">{{query.description ? 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="col d-sm-flex px-3">
|
||||
<div class="col p-0 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>
|
||||
<p class="text-right mb-1" v-if="!query.platforms || !query.platforms.length">--</p>
|
||||
<div class="d-flex m-0" v-else>
|
||||
<img class="d-inline-flex ml-3 logo" src="/images/os-macos-black-24x24@2x.png" alt="macOS" v-if="query.platforms.includes('macOS')"/>
|
||||
<img class="d-inline-flex ml-3 logo" src="/images/os-windows-black-24x24@2x.png" alt="Windows" v-if="query.platforms.includes('Windows')"/>
|
||||
<img class="d-inline-flex ml-3 logo" src="/images/os-linux-black-24x24@2x.png" alt="Linux" v-if="query.platforms.includes('Linux')"/>
|
||||
<img class="d-inline-flex ml-3 logo" src="/images/os-freebsd-black-24x24@2x.png" alt="FreeBSD" v-if="query.platforms.includes('FreeBSD')"/>
|
||||
</div>
|
||||
</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>
|
||||
<p class="m-0">{{query.purpose ? 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-sm-none m-3 divider"></div>
|
||||
<div class="col p-0 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">
|
||||
<span class="mb-1" v-if="!query.contributors || !query.contributors.length">--</span>
|
||||
<div class="d-flex" v-for="contributor in query.contributors">
|
||||
<div class="d-flex m-1 avatar-frame" @click="clickAvatar(contributor)">
|
||||
<img alt="GitHub profile image" :alt="contributor.name" :src="contributor.avatar_url"/>
|
||||
<img alt="a GitHub user avatar" :alt="contributor.name ? contributor.name : contributor.handle" :src="contributor.avatarUrl"/>
|
||||
</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/main/'+queryLibraryYmlRepoPath">View source</a>
|
||||
<h5 class="d-flex align-items-center m-0 mr-3 contribute">Contribute to this page</h5>
|
||||
<p class="d-flex align-items-center m-0 contribute"><a class="d-flex align-items-center text-right m-0 contribute" target="_blank" :href="'https://github.com/fleetdm/fleet/edit/main/'+queryLibraryYmlRepoPath">View source</a></p>
|
||||
</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="d-block d-md-none px-3 pt-5 pb-4 justify-content-center" style="padding-left: 30px; padding-right: 30px;">
|
||||
<h3 class="pb-3 m-0">Query</h3>
|
||||
<code class="pb-3">{{query.query ? 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>
|
||||
<p>{{query.remediation ? query.remediation : 'N/A'}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
125
website/views/pages/query-library.ejs
vendored
125
website/views/pages/query-library.ejs
vendored
@ -1,19 +1,27 @@
|
||||
<div id="query-library" v-cloak>
|
||||
|
||||
<div class="d-flex justify-content-center">
|
||||
<div class="col-6-grow justify-content-center my-5 library">
|
||||
<div class="container justify-content-center library">
|
||||
<h2 class="mb-3">Standard query library</h2>
|
||||
<p class="pb-3 description">Fleet's standard query library includes a growing collection of useful queries for organizations deploying Fleet and osquery.</p>
|
||||
<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 class="d-md-none">
|
||||
<div class="d-flex">
|
||||
<div class="input-group search-mobile">
|
||||
<div class="input-group-prepend border-right-0">
|
||||
<span class="input-group-text bg-transparent border-right-0 pl-2 pr-1"><img class="search" alt="search"
|
||||
src="/images/icon-search-16x16@2x.png"></span>
|
||||
</div>
|
||||
<input class="form-control border-left-0 px-0" placeholder="Search queries" aria-label="Search queries"
|
||||
v-model="inputTextValue" @keydown.self="delayInput(setSearchString, 400, 'defaultTimer')()" />
|
||||
</div>
|
||||
</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="all queries" selected>All queries</option>
|
||||
<option value="informational">Informational queries</option>
|
||||
<option value="detection">Detection queries</option>
|
||||
</select>
|
||||
</div>
|
||||
@ -21,40 +29,64 @@
|
||||
<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="all platforms" selected>All platforms</option>
|
||||
<option value="macOS">macOS</option>
|
||||
<option value="Windows">Windows</option>
|
||||
<option value="Linux">Linux</option>
|
||||
<option value="FreeBSD">FreeBSD</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 select-purpose" v-model="selectedPurpose">
|
||||
<option value="all" selected>all queries</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 select-platform" 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>
|
||||
</span>
|
||||
<div class="filter-and-search-bar d-none d-md-flex row align-items-center justify-content-between mb-3">
|
||||
<div class="d-flex px-0 align-items-center filters">
|
||||
<div class="d-flex">
|
||||
<div class="dropdown flex-wrap filter purpose">
|
||||
<p class="d-inline-flex mb-0">Show</p>
|
||||
<button class="btn btn-secondary btn-sm dropdown-toggle px-1 py-0" type="button"
|
||||
id="dropdownMenuSelectPurpose" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
{{selectedPurpose}}
|
||||
</button>
|
||||
<div class="dropdown-menu px-2 py-1" aria-labelledby="dropdownMenuSelectPurpose">
|
||||
<button class="dropdown-item p-3" type="button" @click="clickSelectPurpose('all queries')">all
|
||||
queries</button>
|
||||
<button class="dropdown-item p-3" type="button"
|
||||
@click="clickSelectPurpose('informational')">informational</button>
|
||||
<button class="dropdown-item p-3" type="button"
|
||||
@click="clickSelectPurpose('detection')">detection</button>
|
||||
</div>
|
||||
</div>
|
||||
<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 class="d-flex">
|
||||
<div class="dropdown d-flex flex-wrap filter platform">
|
||||
<p class="d-inline-flex flex-wrap pl-1 mb-0">compatible with</p>
|
||||
<button class="btn btn-secondary btn-sm dropdown-toggle pl-2 pr-1 py-0" type="button"
|
||||
id="dropdownMenuSelectPlatform" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
{{selectedPlatform}}
|
||||
</button>
|
||||
<div class="dropdown-menu px-2 py-1" aria-labelledby="dropdownMenuSelectPlatform">
|
||||
<button class="dropdown-item p-3" type="button" @click="clickSelectPlatform('all platforms')">all platforms</button>
|
||||
<button class="dropdown-item p-3" type="button" @click="clickSelectPlatform('macOS')">macOS</button>
|
||||
<button class="dropdown-item p-3" type="button"
|
||||
@click="clickSelectPlatform('Windows')">Windows</button>
|
||||
<button class="dropdown-item p-3" type="button" @click="clickSelectPlatform('Linux')">Linux</button>
|
||||
<button class="dropdown-item p-3" type="button"
|
||||
@click="clickSelectPlatform('FreeBSD')">FreeBSD</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex px-0">
|
||||
<div class="d-flex justify-content-end">
|
||||
<div class="input-group search">
|
||||
<div class="input-group-prepend border-right-0 search">
|
||||
<span class="input-group-text bg-transparent border-right-0 pl-2 pr-1"><img class="search"
|
||||
alt="search" src="/images/icon-search-16x16@2x.png"></span>
|
||||
</div>
|
||||
<input class="form-control border-left-0 px-0" placeholder="Search queries" aria-label="Search queries"
|
||||
v-model="inputTextValue" @keydown.self="delayInput(setSearchString, 400, 'defaultTimer')()" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -65,25 +97,34 @@
|
||||
<div class="card results" @click="clickCard(query.slug)">
|
||||
<div class="card-body query-card">
|
||||
<div class="row justify-content-between align-items-center">
|
||||
<div class="col-sm-9 col-md-10">
|
||||
<div class="col-sm-9 col-md-9">
|
||||
<h5 class="card-title m-0">{{query.name}}</h5>
|
||||
<p class="font-italic mb-1 p-0 description">{{query.description}}</p>
|
||||
<div class="contributors" v-if="query.contributors && query.contributors.length">
|
||||
<div class="d-flex mb-2 mb-sm-1 align-items-center">
|
||||
<div v-for="contributor in query.contributors.split(',')">
|
||||
<div v-for="contributor in query.contributors">
|
||||
<div class="d-flex m-1 avatar-frame" @click="clickAvatar(contributor)">
|
||||
<img alt="GitHub profile image" :alt="contributor" :src="getAvatarUrl(contributorsDictionary[contributor])"/>
|
||||
<img alt="a GitHub user avatar" :alt="contributor" :src="contributor.avatarUrl" />
|
||||
</div>
|
||||
</div>
|
||||
<p class="mb-0 ml-1">contributed by {{getContributorsString(query.contributors.split(','), contributorsDictionary)}}</p>
|
||||
<p class="mb-0 ml-1">contributed by {{getContributorsString(query.contributors)}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3 col-md-2">
|
||||
<div class="col-sm-3 col-md-auto">
|
||||
<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>
|
||||
<img class="d-inline-flex mr-1 mr-sm-0 ml-sm-1 ml-md-2 logo"
|
||||
src="/images/os-macos-black-16x16@2x.png" alt="macOS"
|
||||
v-if="query.platforms.includes('macOS')" />
|
||||
<img class="d-inline-flex mr-1 mr-sm-0 ml-sm-1 ml-md-2 logo"
|
||||
src="/images/os-windows-black-16x16@2x.png" alt="Windows"
|
||||
v-if="query.platforms.includes('Windows')" />
|
||||
<img class="d-inline-flex mr-1 mr-ms-0 ml-sm-1 ml-md-2 logo"
|
||||
src="/images/os-linux-black-16x16@2x.png" alt="Linux"
|
||||
v-if="query.platforms.includes('Linux')" />
|
||||
<img class="d-inline-flex mr-1 mr-sm-0 ml-sm-1 ml-md-2 logo"
|
||||
src="/images/os-freebsd-black-16x16@2x.png" alt="FreeBSD"
|
||||
v-if="query.platforms.includes('FreeBSD')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -99,7 +140,11 @@
|
||||
<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/edit/main/docs/1-Using-Fleet/standard-query-library/standard-query-library.yml" >over on GitHub</a>.</p>
|
||||
<p><strong>Want to add your own query?</strong> Please submit a pull request
|
||||
<a href="https://github.com/fleetdm/fleet/edit/main/docs/1-Using-Fleet/standard-query-library/standard-query-library.yml">
|
||||
over on GitHub
|
||||
</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user