From 8dac783c50690e0a5a4b9581a2b6624a6ed9cfd5 Mon Sep 17 00:00:00 2001 From: Roberto Dip Date: Tue, 2 Apr 2024 11:21:17 -0300 Subject: [PATCH 01/60] increase TUF expiration warning by one day hopefully this will get obsolete before we have time to use it, but just in case this increments the warning time to give us more leeway. --- .github/workflows/check-tuf-timestamps.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check-tuf-timestamps.yml b/.github/workflows/check-tuf-timestamps.yml index f5c01ec136..c55de6e2b9 100644 --- a/.github/workflows/check-tuf-timestamps.yml +++ b/.github/workflows/check-tuf-timestamps.yml @@ -38,11 +38,11 @@ jobs: run: | expires=$(curl -s http://tuf.fleetctl.com/timestamp.json | jq -r '.signed.expires' | cut -c 1-10) today=$(date "+%Y-%m-%d") - tomorrow=$(date -d "$today + 1 day" "+%Y-%m-%d") + warning_at=$(date -d "$today + 2 day" "+%Y-%m-%d") expires_sec=$(date -d "$expires" "+%s") - tomorrow_sec=$(date -d "$tomorrow" "+%s") + warning_at_sec=$(date -d "$warning_at" "+%s") - if [ "$expires_sec" -le "$tomorrow_sec" ]; then + if [ "$expires_sec" -le "$warning_at_sec" ]; then exit 1 else exit 0 From b0ed358207743a72dccbc087a72074bd9dfbe4dc Mon Sep 17 00:00:00 2001 From: Mike McNeil Date: Tue, 2 Apr 2024 10:26:05 -0500 Subject: [PATCH 02/60] =?UTF-8?q?Handbook:=20revops=20=C2=BB=20digital=20e?= =?UTF-8?q?xperience=20(#17992)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Looks like we missed some updates here at prev transition --- handbook/business-operations/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/handbook/business-operations/README.md b/handbook/business-operations/README.md index 351c55d71b..32ad5d2006 100644 --- a/handbook/business-operations/README.md +++ b/handbook/business-operations/README.md @@ -15,7 +15,7 @@ This handbook page details processes specific to working [with](#what-we-do) and ## Responsibilities -The Business Operations department is directly responsible for the functions of all Finance, People, Legal, IT, and Revenue Operations (RevOps). +The Business Operations department is directly responsible for finance + invoicing, people operations, legal + deal desk, and corporate information technology (IT). ### Run payroll Many of these processes are automated, but it's vital to check Gusto and Plane manually for accuracy. @@ -24,10 +24,10 @@ Many of these processes are automated, but it's vital to check Gusto and Plane m | Payroll type | What to use | DRI | |:-----------------------------|:-----------------------------|:-----------------------------| -| [Commissions and ramp](https://fleetdm.com/handbook/business-operations#run-us-commission-payroll) | "Off-cycle" payroll | Head of Revenue Operations +| [Commissions and ramp](https://fleetdm.com/handbook/business-operations#run-us-commission-payroll) | "Off-cycle" payroll | Head of Business Operations | Sign-on bonus | "Bonus" payroll | Head of Business Operations | Performance bonus | "Bonus" payroll | Head of Business Operations -| Accelerations (quarterly) | "Off-cycle" payroll | Head of Revenue Operations +| Accelerations (quarterly) | "Off-cycle" payroll | Head of Business Operations | [US contractor payroll](https://fleetdm.com/handbook/business-operations#run-us-contractor-payroll) | "Off-cycle" payroll | Head of Business Operations ### Reconcile monthly recurring expenses @@ -109,7 +109,7 @@ For Fleet's US contractors, running payroll is a manual process: - Sync hours and run contractor payroll. -### Grant role-specific license to a team member (RevOps) +### Grant role-specific license to a team member Certain new team members, especially in go-to-market (GTM) roles, will need paid access to paid tools like Salesforce and LinkedIn Sales Navigator immediately on their first dayΒ with the company. Gong licenses that other departments need may [request them from BizOps](https://fleetdm.com/handbook/business-operations#contact-us) and we will make sure there is no license redundancy in that department. The table below can be used to determine which paid licenses they will need, based on their role: | Role | Salesforce CRM | Salesforce "Inbox" | LinkedIn _(paid)_ | Gong _(paid)_ | Zoom _(paid)_| From 04f7682c5063a90e025f3585486567900d3d32a2 Mon Sep 17 00:00:00 2001 From: JD Date: Tue, 2 Apr 2024 08:26:41 -0700 Subject: [PATCH 03/60] Article: sysadmin diaries passcode profiles (#17994) Article: Sysadmin diaries: passcode profiles https://github.com/fleetdm/confidential/issues/5587 --------- Co-authored-by: Eric --- .../sysadmin-diaries-passcode-profiles.md | 62 ++++++++++++++++++ .../articles/sysadmin-diaries-1600x900@2x.png | Bin 0 -> 20049 bytes ...sadmin-diaries-change-later-231x160@2x.png | Bin 0 -> 17888 bytes ...ies-password-policy-updated-689x140@2x.png | Bin 0 -> 42191 bytes 4 files changed, 62 insertions(+) create mode 100644 articles/sysadmin-diaries-passcode-profiles.md create mode 100644 website/assets/images/articles/sysadmin-diaries-1600x900@2x.png create mode 100644 website/assets/images/articles/sysadmin-diaries-change-later-231x160@2x.png create mode 100644 website/assets/images/articles/sysadmin-diaries-password-policy-updated-689x140@2x.png diff --git a/articles/sysadmin-diaries-passcode-profiles.md b/articles/sysadmin-diaries-passcode-profiles.md new file mode 100644 index 0000000000..c40cba97f8 --- /dev/null +++ b/articles/sysadmin-diaries-passcode-profiles.md @@ -0,0 +1,62 @@ +# Sysadmin diaries: passcode profiles + +![Sysadmin diaries: passcode profiles](../website/assets/images/articles/sysadmin-diaries-1600x900@2x.png) + +Passcode MDM profiles do not work the way we might think they should. We recently onboarded a new Fleetie, which is always an opportunity to _eat our own dog food_ when using Fleet for device management. + +This is the first in a series of things we encounter when managing our own devices (aka hosts). Fleet is an open-source and open-core company. Our [handbook](https://fleetdm.com/handbook) is public for everyone to view (and improve!). The [configuration policies](https://github.com/fleetdm/fleet-gitops) we apply to our devices reside in our public git repo. Today, we are looking at Fleet's password policy for macOS devices, which utilizes the [passcode policy payload](https://developer.apple.com/documentation/devicemanagement/passcode). + +The user set up their new computer, created an account, and used a passcode that does not meet Fleet's passcode policy, namely a length of 10 characters. Sometime after that, the MDM profiles were delivered to the host. The expected behavior would be that the user would be prompted to enter a compliant passcode upon the next login. + +What happens instead with this policy is that after login, the user is prompted with a "Password Policy Updated" notification. + + +![Password policy updated notification](../website/assets/images/articles/sysadmin-diaries-password-policy-updated-689x140@2x.png +"Password policy updated") + + + This notification comes with the ability just to ignore it: Change Later or just dismiss the dialog. + + +![Password policy options > change now… or change +later](../website/assets/images/articles/sysadmin-diaries-change-later-231x160@2x.png "Password +policy > change later") + + +A quick search of the [Mac Admins Slack](https://www.macadmins.org/) confirmed my suspicions. The non-compliant passcode will remain indefinitely, and the profile requirements are only enforced on the next reset or new account creation. + + +### Why did this happen, and how do we solve it? + +We discovered that the policy was not applied because Fleet needed to lock out account creation before all the policies had been successfully applied to the host. We have corrected this in [Fleet 4.48.0](https://fleetdm.com/releases/fleet-4.48.0), but how do we resolve this issue with an existing enrolled device and a change in the organization's password policy? + + +Do we add the `changeAtNextAuth` key? A read of Apple's documentation means every user with this policy must reset their password on the next authentication. That could be highly disruptive. And, if the policy is redeployed for any reason, could institute a password reset to every host in that team. + + +
+changeAtNextAuth (boolean) + +If true, the system causes a password reset to occur the next time the user tries to authenticate. If this key is set in a device profile, the setting takes effect for all users, and admin authentications may fail until the admin user password is also reset. Available in macOS 10.13 and later. +
+ +Another solution is to use Fleet's remote script execution capability to trigger a one-off password reset on the host. + +``` +pwpolicy -u "501" -setpolicy "newPasswordRequired=1" +``` + +This will require the user to reset their password upon the next login to the host. This is likely the best solution in this situation, as it can be applied on an individual host basis. + +In wrapping up this exploration into the intricacies of passcode profiles and their challenges, Fleet's open-source nature allows us to share these experiences and collectively seek solutions that enhance our understanding and implementation of device management policies. Let's continue the conversation. [Join us on Slack](https://fleetdm.com/support) and let us know how you might solve this issue and what device management problems you want to solve. + + + + + + + + + + + diff --git a/website/assets/images/articles/sysadmin-diaries-1600x900@2x.png b/website/assets/images/articles/sysadmin-diaries-1600x900@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0c16f63536721d25fe05f3ff2ef46fa80cf76d27 GIT binary patch literal 20049 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 literal 0 HcmV?d00001 diff --git a/website/assets/images/articles/sysadmin-diaries-change-later-231x160@2x.png b/website/assets/images/articles/sysadmin-diaries-change-later-231x160@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7752817b9240de99c7fdd6381866a1b985d41bab GIT binary patch literal 17888 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 o#q~daIlxAs*XyXl1 Date: Wed, 3 Apr 2024 01:24:16 +0900 Subject: [PATCH 04/60] Update README.md (#17979) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b5dded9eae..36784d5fcf 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ The Fleet community is full of [kind and helpful people](https://fleetdm.com/han The landscape of cybersecurity and IT is too complex. Let's open it up. -Contributions are welcome, whether you answer questions on [Slack](#chat) / [GitHub](https://github.com/fleetdm/fleet/issues) / [StackOverflow](https://stackoverflow.com/search?q=osquery) / [LinkedIn](https://linkedin.com/company/fleetdm) / [Twitter](https://twitter.com/fleetctl), improve the documentation or [website](./website), write a tutorial, give a talk at a conference or local meetup, give an [interview on a podcast](https://fleetdm.com/podcasts), troubleshoot reported issues, or [submit a patch](https://fleetdm.com/docs/contributing/contributing). The Fleet code of conduct is [on GitHub](https://github.com/fleetdm/fleet/blob/main/CODE_OF_CONDUCT.md). +Contributions are welcome, whether you answer questions on [Slack](https://fleetdm.com/slack) / [GitHub](https://github.com/fleetdm/fleet/issues) / [StackOverflow](https://stackoverflow.com/search?q=osquery) / [LinkedIn](https://linkedin.com/company/fleetdm) / [Twitter](https://twitter.com/fleetctl), improve the documentation or [website](./website), write a tutorial, give a talk at a conference or local meetup, give an [interview on a podcast](https://fleetdm.com/podcasts), troubleshoot reported issues, or [submit a patch](https://fleetdm.com/docs/contributing/contributing). The Fleet code of conduct is [on GitHub](https://github.com/fleetdm/fleet/blob/main/CODE_OF_CONDUCT.md). +Want to hire? Use these steps to hire a [fleetie, not a consultant](https://fleetdm.com/handbook/company/leadership#who-isnt-a-consultant). Here's how to open up a new position on the core team: 1. **Propose headcount:** Add the proposed position to ["πŸ§‘β€πŸš€ Fleeties"](https://docs.google.com/spreadsheets/d/1OSLn-ZCbGSjPusHPiR5dwQhheH1K8-xqyZdsOe9y7qc/edit#gid=0) in an empty row (but using one of the existing IDs. Unsure? Ask for help.) Be sure to include job title, manager, and department. Set the start date to the first Monday of the next month (This position is still only proposed (not approved), but would make it easier for the approver to have the date set). 2. **Propose job description:** Copy, personalize, and publish the job description: From 0bca2416806b3e5cfa246746b0cf2a5b9b9f617e Mon Sep 17 00:00:00 2001 From: Victor Lyuboslavsky Date: Tue, 2 Apr 2024 16:03:51 -0500 Subject: [PATCH 09/60] Mark calendar webhook as errored so that it doesn't get stuck in pending and keeps logging warnings. --- server/fleet/calendar_events.go | 1 + server/service/osquery.go | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/server/fleet/calendar_events.go b/server/fleet/calendar_events.go index 3152ee65bb..4574d23725 100644 --- a/server/fleet/calendar_events.go +++ b/server/fleet/calendar_events.go @@ -18,6 +18,7 @@ const ( CalendarWebhookStatusNone CalendarWebhookStatus = iota CalendarWebhookStatusPending CalendarWebhookStatusSent + CalendarWebhookStatusError ) type HostCalendarEvent struct { diff --git a/server/service/osquery.go b/server/service/osquery.go index 379afafd6a..31e4e33d27 100644 --- a/server/service/osquery.go +++ b/server/service/osquery.go @@ -1133,16 +1133,22 @@ func processCalendarPolicies( now := time.Now() if now.Before(calendarEvent.StartTime) { level.Warn(logger).Log("msg", "results came too early", "now", now, "start_time", calendarEvent.StartTime) + if err = ds.UpdateHostCalendarWebhookStatus(context.Background(), host.ID, fleet.CalendarWebhookStatusError); err != nil { + level.Error(logger).Log("msg", "mark webhook as errored early", "err", err) + } return nil } // // TODO(lucas): Discuss. // - const allowedTimeBeforeEndTime = 5 * time.Minute // up to 5 minutes before the end_time + const allowedTimeRelativeToEndTime = 5 * time.Minute // up to 5 minutes after the end_time to allow for short (0-time) event times - if now.After(calendarEvent.EndTime.Add(-allowedTimeBeforeEndTime)) { + if now.After(calendarEvent.EndTime.Add(allowedTimeRelativeToEndTime)) { level.Warn(logger).Log("msg", "results came too late", "now", now, "end_time", calendarEvent.EndTime) + if err = ds.UpdateHostCalendarWebhookStatus(context.Background(), host.ID, fleet.CalendarWebhookStatusError); err != nil { + level.Error(logger).Log("msg", "mark webhook as errored late", "err", err) + } return nil } From a1fa232aca7518c4fb6f4cfd0c7ba919644ed6c7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 3 Apr 2024 06:26:35 -0300 Subject: [PATCH 10/60] Update versions of fleetd components in Fleet's TUF [automated] (#18029) Automated change from [GitHub action](https://github.com/fleetdm/fleet/actions/workflows/fleetd-tuf.yml). Co-authored-by: lucasmrod --- orbit/TUF.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orbit/TUF.md b/orbit/TUF.md index 6eb92922b4..fc164d0d15 100644 --- a/orbit/TUF.md +++ b/orbit/TUF.md @@ -19,6 +19,6 @@ Following are the currently deployed versions of fleetd components on the `stabl |--------------|--------|--------|---------| | orbit | 1.22.0 | 1.22.0 | 1.22.0 | | desktop | 1.22.0 | 1.22.0 | 1.22.0 | -| osqueryd | 5.12.0 | 5.12.0 | 5.12.0 | +| osqueryd | 5.12.1 | 5.12.1 | 5.12.1 | | nudge | - | - | - | | swiftDialog | - | - | - | From a3f3230aa97d6fdbbe4ee7989ab1a2d2715a6197 Mon Sep 17 00:00:00 2001 From: Gabriel Hernandez Date: Wed, 3 Apr 2024 12:33:38 +0100 Subject: [PATCH 11/60] fix up some lint warning that show in github (#17997) quick PR to clean up some of the warning that show up in github PRs. --- frontend/components/AddHostsModal/AddHostsModal.tsx | 2 +- .../AddHostsModal/PlatformWrapper/PlatformWrapper.tsx | 4 ++-- frontend/components/App/App.tsx | 3 +-- frontend/components/ClickableUrls/ClickableUrls.tsx | 1 + .../components/FileUploader/FileUploader.stories.tsx | 2 +- frontend/components/FlashMessage/FlashMessage.tsx | 9 ++++++++- frontend/components/FleetAce/FleetAce.tsx | 2 +- 7 files changed, 15 insertions(+), 8 deletions(-) diff --git a/frontend/components/AddHostsModal/AddHostsModal.tsx b/frontend/components/AddHostsModal/AddHostsModal.tsx index 7d74f7cfc3..d4cb3dfdf9 100644 --- a/frontend/components/AddHostsModal/AddHostsModal.tsx +++ b/frontend/components/AddHostsModal/AddHostsModal.tsx @@ -39,7 +39,7 @@ const AddHostsModal = ({ data: certificate, error: fetchCertificateError, isFetching: isFetchingCertificate, - } = useQuery( + } = useQuery( ["certificate"], () => configAPI.loadCertificate(), { diff --git a/frontend/components/AddHostsModal/PlatformWrapper/PlatformWrapper.tsx b/frontend/components/AddHostsModal/PlatformWrapper/PlatformWrapper.tsx index e81d9de244..4f333918bd 100644 --- a/frontend/components/AddHostsModal/PlatformWrapper/PlatformWrapper.tsx +++ b/frontend/components/AddHostsModal/PlatformWrapper/PlatformWrapper.tsx @@ -55,9 +55,9 @@ const platformSubNav: IPlatformSubNav[] = [ interface IPlatformWrapperProps { enrollSecret: string; onCancel: () => void; - certificate: any; + certificate?: string; isFetchingCertificate: boolean; - fetchCertificateError: any; + fetchCertificateError: string | null; config: IConfig | null; } diff --git a/frontend/components/App/App.tsx b/frontend/components/App/App.tsx index 136480369a..73c663d4d3 100644 --- a/frontend/components/App/App.tsx +++ b/frontend/components/App/App.tsx @@ -1,4 +1,4 @@ -import React, { FC, ReactNode, useContext, useEffect, useState } from "react"; +import React, { FC, useContext, useEffect, useState } from "react"; import { AxiosResponse } from "axios"; import { QueryClient, @@ -152,7 +152,6 @@ const App = ({ children, location }: IAppProps): JSX.Element => { setEnrollSecret(spec.secrets); } catch (error) { console.error(error); - return false; } }; diff --git a/frontend/components/ClickableUrls/ClickableUrls.tsx b/frontend/components/ClickableUrls/ClickableUrls.tsx index 444dee0f3d..9e8294a802 100644 --- a/frontend/components/ClickableUrls/ClickableUrls.tsx +++ b/frontend/components/ClickableUrls/ClickableUrls.tsx @@ -31,6 +31,7 @@ const ClickableUrls = ({ text, className }: IClickableUrls): JSX.Element => { const textWithLinks = (

); diff --git a/frontend/components/FileUploader/FileUploader.stories.tsx b/frontend/components/FileUploader/FileUploader.stories.tsx index 68de42c15d..f35ea77122 100644 --- a/frontend/components/FileUploader/FileUploader.stories.tsx +++ b/frontend/components/FileUploader/FileUploader.stories.tsx @@ -12,7 +12,7 @@ const meta: Meta = { accept: ".pdf", isLoading: false, onFileUpload: () => { - alert("File uploaded!"); + console.log("File uploaded!"); }, }, }; diff --git a/frontend/components/FlashMessage/FlashMessage.tsx b/frontend/components/FlashMessage/FlashMessage.tsx index 102b2c3601..bef7fb8292 100644 --- a/frontend/components/FlashMessage/FlashMessage.tsx +++ b/frontend/components/FlashMessage/FlashMessage.tsx @@ -58,7 +58,14 @@ const FlashMessage = ({ } return undefined; // No cleanup when we don't set a timeout. - }, [notification, alertType, isVisible, setHide]); + }, [ + notification, + alertType, + isVisible, + setHide, + isPersistent, + onRemoveFlash, + ]); if (hide || !isVisible) { return null; diff --git a/frontend/components/FleetAce/FleetAce.tsx b/frontend/components/FleetAce/FleetAce.tsx index 5a6f6ba7bc..25d8856e8a 100644 --- a/frontend/components/FleetAce/FleetAce.tsx +++ b/frontend/components/FleetAce/FleetAce.tsx @@ -198,7 +198,7 @@ const FleetAce = ({ onLoad && onLoad(editor); }; - const onBlurHandler = (event: any, editor?: IAceEditor): void => { + const onBlurHandler = (event: unknown, editor?: IAceEditor): void => { onBlur && onBlur(editor); }; From 3d260fa9ab125c4343c3751a8b5db84fc01fecca Mon Sep 17 00:00:00 2001 From: Lucas Manuel Rodriguez Date: Wed, 3 Apr 2024 10:57:28 -0300 Subject: [PATCH 12/60] Bump osqueryd version to 5.12.1 (#18028) Bumping version of osqueryd for releasing 5.12.1 to the `edge` channel. --- .github/workflows/generate-osqueryd-targets.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/generate-osqueryd-targets.yml b/.github/workflows/generate-osqueryd-targets.yml index 0af9f64bea..46d079828a 100644 --- a/.github/workflows/generate-osqueryd-targets.yml +++ b/.github/workflows/generate-osqueryd-targets.yml @@ -24,7 +24,7 @@ defaults: shell: bash env: - OSQUERY_VERSION: 5.12.0 + OSQUERY_VERSION: 5.12.1 permissions: contents: read From e3deceeb6d31336dc2a0687e3172fe0c539087dd Mon Sep 17 00:00:00 2001 From: Zach Wasserman Date: Wed, 3 Apr 2024 09:13:04 -0700 Subject: [PATCH 13/60] Add `parse_json`, `parse_jsonl`, `parse_xml`, and `parse_ini` tables to fleetd (#18035) For #17577 # Checklist for submitter If some of the following don't apply, delete the relevant line. - [x] Changes file added for user-visible changes in `changes/` or `orbit/changes/`. See [Changes files](https://fleetdm.com/docs/contributing/committing-changes#changes-files) for more information. - [x] Manual QA for all new/changed functionality - For Orbit and Fleet Desktop changes: - [x] Manual QA must be performed in the three main OSs, macOS, Windows and Linux. (performed only on macOS) --- orbit/changes/dataflatten-tables | 1 + orbit/pkg/table/extension.go | 8 ++ schema/osquery_fleet_schema.json | 180 +++++++++++++++++++++++++++++++ schema/tables/parse_ini.yml | 29 +++++ schema/tables/parse_json.yml | 29 +++++ schema/tables/parse_jsonl.yml | 29 +++++ schema/tables/parse_xml.yml | 29 +++++ 7 files changed, 305 insertions(+) create mode 100644 orbit/changes/dataflatten-tables create mode 100644 schema/tables/parse_ini.yml create mode 100644 schema/tables/parse_json.yml create mode 100644 schema/tables/parse_jsonl.yml create mode 100644 schema/tables/parse_xml.yml diff --git a/orbit/changes/dataflatten-tables b/orbit/changes/dataflatten-tables new file mode 100644 index 0000000000..d2ec646ff4 --- /dev/null +++ b/orbit/changes/dataflatten-tables @@ -0,0 +1 @@ +- Add `parse_json`, `parse_jsonl`, `parse_xml`, and `parse_ini` tables. diff --git a/orbit/pkg/table/extension.go b/orbit/pkg/table/extension.go index b600c7e7d2..2f8d5c1db1 100644 --- a/orbit/pkg/table/extension.go +++ b/orbit/pkg/table/extension.go @@ -9,6 +9,7 @@ import ( "time" "github.com/fleetdm/fleet/v4/orbit/pkg/table/cryptoinfotable" + "github.com/fleetdm/fleet/v4/orbit/pkg/table/dataflattentable" "github.com/fleetdm/fleet/v4/orbit/pkg/table/firefox_preferences" "github.com/fleetdm/fleet/v4/orbit/pkg/table/sntp_request" "github.com/macadmins/osquery-extension/tables/chromeuserprofiles" @@ -134,6 +135,13 @@ func OrbitDefaultTables() []osquery.OsqueryPlugin { firefox_preferences.TablePlugin(osqueryLogger), cryptoinfotable.TablePlugin(osqueryLogger), + + // Additional data format tables + dataflattentable.TablePlugin(osqueryLogger, dataflattentable.JsonType), // table name is "parse_json" + dataflattentable.TablePlugin(osqueryLogger, dataflattentable.JsonlType), // table name is "parse_jsonl" + dataflattentable.TablePlugin(osqueryLogger, dataflattentable.XmlType), // table name is "parse_xml" + dataflattentable.TablePlugin(osqueryLogger, dataflattentable.IniType), // table name is "parse_ini" + } return plugins } diff --git a/schema/osquery_fleet_schema.json b/schema/osquery_fleet_schema.json index c4b7de39e2..7262cfba22 100644 --- a/schema/osquery_fleet_schema.json +++ b/schema/osquery_fleet_schema.json @@ -18786,6 +18786,186 @@ ], "fleetRepoUrl": "https://github.com/fleetdm/fleet/blob/main/schema/tables/package_receipts.yml" }, + { + "name": "parse_ini", + "notes": "This table is not a core osquery table. It is included as part of [Fleetd](https://fleetdm.com/docs/using-fleet/orbit), the osquery manager from Fleet. Fleetd can be built with [fleetctl](https://fleetdm.com/docs/using-fleet/adding-hosts#osquery-installer).", + "description": "Parse a file as INI configuration.", + "platforms": [ + "darwin", + "windows", + "linux" + ], + "evented": false, + "columns": [ + { + "name": "path", + "description": "Path of the file to read.", + "required": true, + "type": "text" + }, + { + "name": "fullkey", + "description": "Key including any parent keys.", + "type": "text", + "required": false + }, + { + "name": "parent", + "description": "Parent key when keys are nested in the document.", + "required": false, + "type": "text" + }, + { + "name": "key", + "description": "JSON key or array index.", + "required": false, + "type": "text" + }, + { + "name": "value", + "description": "JSON value", + "required": false, + "type": "text" + } + ], + "url": "https://fleetdm.com/tables/parse_ini", + "fleetRepoUrl": "https://github.com/fleetdm/fleet/blob/main/schema/tables/parse_ini.yml" + }, + { + "name": "parse_json", + "notes": "This table is not a core osquery table. It is included as part of [Fleetd](https://fleetdm.com/docs/using-fleet/orbit), the osquery manager from Fleet. Fleetd can be built with [fleetctl](https://fleetdm.com/docs/using-fleet/adding-hosts#osquery-installer).", + "description": "Parses an entire file as JSON. See `parse_jsonl` where multiple JSON documents are supported.", + "platforms": [ + "darwin", + "windows", + "linux" + ], + "evented": false, + "columns": [ + { + "name": "path", + "description": "Path of the file to read.", + "required": true, + "type": "text" + }, + { + "name": "fullkey", + "description": "Same as `key` in this table. See `parse_jsonl` where multiple JSON documents are supported.", + "required": false, + "type": "text" + }, + { + "name": "parent", + "description": "Parent key when keys are nested in the document.", + "required": false, + "type": "text" + }, + { + "name": "key", + "description": "JSON key or array index.", + "required": false, + "type": "text" + }, + { + "name": "value", + "description": "JSON value", + "required": false, + "type": "text" + } + ], + "url": "https://fleetdm.com/tables/parse_json", + "fleetRepoUrl": "https://github.com/fleetdm/fleet/blob/main/schema/tables/parse_json.yml" + }, + { + "name": "parse_jsonl", + "notes": "This table is not a core osquery table. It is included as part of [Fleetd](https://fleetdm.com/docs/using-fleet/orbit), the osquery manager from Fleet. Fleetd can be built with [fleetctl](https://fleetdm.com/docs/using-fleet/adding-hosts#osquery-installer).", + "description": "Parses each line of a file as a separate JSON document. See `parse_json` to treat an entire file as a single JSON document.", + "platforms": [ + "darwin", + "windows", + "linux" + ], + "evented": false, + "columns": [ + { + "name": "path", + "description": "Path of the file to read.", + "required": true, + "type": "text" + }, + { + "name": "fullkey", + "description": "Key including any parent keys or document indices.", + "required": false, + "type": "text" + }, + { + "name": "parent", + "description": "Parent key when keys are nested in the document.", + "required": false, + "type": "text" + }, + { + "name": "key", + "description": "INI key", + "required": false, + "type": "text" + }, + { + "name": "value", + "description": "INI value", + "required": false, + "type": "text" + } + ], + "url": "https://fleetdm.com/tables/parse_jsonl", + "fleetRepoUrl": "https://github.com/fleetdm/fleet/blob/main/schema/tables/parse_jsonl.yml" + }, + { + "name": "parse_xml", + "notes": "This table is not a core osquery table. It is included as part of [Fleetd](https://fleetdm.com/docs/using-fleet/orbit), the osquery manager from Fleet. Fleetd can be built with [fleetctl](https://fleetdm.com/docs/using-fleet/adding-hosts#osquery-installer).", + "description": "Parses a file as an XML document.", + "platforms": [ + "darwin", + "windows", + "linux" + ], + "evented": false, + "columns": [ + { + "name": "path", + "description": "Path of the file to read.", + "required": true, + "type": "text" + }, + { + "name": "fullkey", + "description": "Key including any parent keys.", + "required": false, + "type": "text" + }, + { + "name": "parent", + "description": "Parent key when keys are nested in the document.", + "required": false, + "type": "text" + }, + { + "name": "key", + "description": "XML key", + "required": false, + "type": "text" + }, + { + "name": "value", + "description": "XML value", + "required": false, + "type": "text" + } + ], + "url": "https://fleetdm.com/tables/parse_xml", + "fleetRepoUrl": "https://github.com/fleetdm/fleet/blob/main/schema/tables/parse_xml.yml" + }, { "name": "password_policy", "description": "Password Policies for macOS.", diff --git a/schema/tables/parse_ini.yml b/schema/tables/parse_ini.yml new file mode 100644 index 0000000000..3d33596f39 --- /dev/null +++ b/schema/tables/parse_ini.yml @@ -0,0 +1,29 @@ +name: parse_ini +notes: This table is not a core osquery table. It is included as part of [Fleetd](https://fleetdm.com/docs/using-fleet/orbit), the osquery manager from Fleet. Fleetd can be built with [fleetctl](https://fleetdm.com/docs/using-fleet/adding-hosts#osquery-installer). +description: Parse a file as INI configuration. +platforms: + - darwin + - windows + - linux +evented: false +columns: + - name: path + description: Path of the file to read. + required: true + type: text + - name: fullkey + description: Key including any parent keys. + type: text + required: false + - name: parent + description: Parent key when keys are nested in the document. + required: false + type: text + - name: key + description: JSON key or array index. + required: false + type: text + - name: value + description: JSON value + required: false + type: text \ No newline at end of file diff --git a/schema/tables/parse_json.yml b/schema/tables/parse_json.yml new file mode 100644 index 0000000000..7a0e9a339a --- /dev/null +++ b/schema/tables/parse_json.yml @@ -0,0 +1,29 @@ +name: parse_json +notes: This table is not a core osquery table. It is included as part of [Fleetd](https://fleetdm.com/docs/using-fleet/orbit), the osquery manager from Fleet. Fleetd can be built with [fleetctl](https://fleetdm.com/docs/using-fleet/adding-hosts#osquery-installer). +description: Parses an entire file as JSON. See `parse_jsonl` where multiple JSON documents are supported. +platforms: + - darwin + - windows + - linux +evented: false +columns: + - name: path + description: Path of the file to read. + required: true + type: text + - name: fullkey + description: Same as `key` in this table. See `parse_jsonl` where multiple JSON documents are supported. + required: false + type: text + - name: parent + description: Parent key when keys are nested in the document. + required: false + type: text + - name: key + description: JSON key or array index. + required: false + type: text + - name: value + description: JSON value + required: false + type: text \ No newline at end of file diff --git a/schema/tables/parse_jsonl.yml b/schema/tables/parse_jsonl.yml new file mode 100644 index 0000000000..7aae0eef65 --- /dev/null +++ b/schema/tables/parse_jsonl.yml @@ -0,0 +1,29 @@ +name: parse_jsonl +notes: This table is not a core osquery table. It is included as part of [Fleetd](https://fleetdm.com/docs/using-fleet/orbit), the osquery manager from Fleet. Fleetd can be built with [fleetctl](https://fleetdm.com/docs/using-fleet/adding-hosts#osquery-installer). +description: Parses each line of a file as a separate JSON document. See `parse_json` to treat an entire file as a single JSON document. +platforms: + - darwin + - windows + - linux +evented: false +columns: + - name: path + description: Path of the file to read. + required: true + type: text + - name: fullkey + description: Key including any parent keys or document indices. + required: false + type: text + - name: parent + description: Parent key when keys are nested in the document. + required: false + type: text + - name: key + description: INI key + required: false + type: text + - name: value + description: INI value + required: false + type: text \ No newline at end of file diff --git a/schema/tables/parse_xml.yml b/schema/tables/parse_xml.yml new file mode 100644 index 0000000000..21b3fe2a40 --- /dev/null +++ b/schema/tables/parse_xml.yml @@ -0,0 +1,29 @@ +name: parse_xml +notes: This table is not a core osquery table. It is included as part of [Fleetd](https://fleetdm.com/docs/using-fleet/orbit), the osquery manager from Fleet. Fleetd can be built with [fleetctl](https://fleetdm.com/docs/using-fleet/adding-hosts#osquery-installer). +description: Parses a file as an XML document. +platforms: + - darwin + - windows + - linux +evented: false +columns: + - name: path + description: Path of the file to read. + required: true + type: text + - name: fullkey + description: Key including any parent keys. + required: false + type: text + - name: parent + description: Parent key when keys are nested in the document. + required: false + type: text + - name: key + description: XML key + required: false + type: text + - name: value + description: XML value + required: false + type: text \ No newline at end of file From 3decb682202b7b8882473015ce07780ae56ae45e Mon Sep 17 00:00:00 2001 From: Gabriel Hernandez Date: Wed, 3 Apr 2024 17:25:16 +0100 Subject: [PATCH 14/60] revert accidental merge for FE lint warning fixes (#18036) This reverts commit a3f3230aa97d6fdbbe4ee7989ab1a2d2715a6197. I accidentally merged to main so am reverting this change. --- frontend/components/AddHostsModal/AddHostsModal.tsx | 2 +- .../AddHostsModal/PlatformWrapper/PlatformWrapper.tsx | 4 ++-- frontend/components/App/App.tsx | 3 ++- frontend/components/ClickableUrls/ClickableUrls.tsx | 1 - .../components/FileUploader/FileUploader.stories.tsx | 2 +- frontend/components/FlashMessage/FlashMessage.tsx | 9 +-------- frontend/components/FleetAce/FleetAce.tsx | 2 +- 7 files changed, 8 insertions(+), 15 deletions(-) diff --git a/frontend/components/AddHostsModal/AddHostsModal.tsx b/frontend/components/AddHostsModal/AddHostsModal.tsx index d4cb3dfdf9..7d74f7cfc3 100644 --- a/frontend/components/AddHostsModal/AddHostsModal.tsx +++ b/frontend/components/AddHostsModal/AddHostsModal.tsx @@ -39,7 +39,7 @@ const AddHostsModal = ({ data: certificate, error: fetchCertificateError, isFetching: isFetchingCertificate, - } = useQuery( + } = useQuery( ["certificate"], () => configAPI.loadCertificate(), { diff --git a/frontend/components/AddHostsModal/PlatformWrapper/PlatformWrapper.tsx b/frontend/components/AddHostsModal/PlatformWrapper/PlatformWrapper.tsx index 4f333918bd..e81d9de244 100644 --- a/frontend/components/AddHostsModal/PlatformWrapper/PlatformWrapper.tsx +++ b/frontend/components/AddHostsModal/PlatformWrapper/PlatformWrapper.tsx @@ -55,9 +55,9 @@ const platformSubNav: IPlatformSubNav[] = [ interface IPlatformWrapperProps { enrollSecret: string; onCancel: () => void; - certificate?: string; + certificate: any; isFetchingCertificate: boolean; - fetchCertificateError: string | null; + fetchCertificateError: any; config: IConfig | null; } diff --git a/frontend/components/App/App.tsx b/frontend/components/App/App.tsx index 73c663d4d3..136480369a 100644 --- a/frontend/components/App/App.tsx +++ b/frontend/components/App/App.tsx @@ -1,4 +1,4 @@ -import React, { FC, useContext, useEffect, useState } from "react"; +import React, { FC, ReactNode, useContext, useEffect, useState } from "react"; import { AxiosResponse } from "axios"; import { QueryClient, @@ -152,6 +152,7 @@ const App = ({ children, location }: IAppProps): JSX.Element => { setEnrollSecret(spec.secrets); } catch (error) { console.error(error); + return false; } }; diff --git a/frontend/components/ClickableUrls/ClickableUrls.tsx b/frontend/components/ClickableUrls/ClickableUrls.tsx index 9e8294a802..444dee0f3d 100644 --- a/frontend/components/ClickableUrls/ClickableUrls.tsx +++ b/frontend/components/ClickableUrls/ClickableUrls.tsx @@ -31,7 +31,6 @@ const ClickableUrls = ({ text, className }: IClickableUrls): JSX.Element => { const textWithLinks = (
); diff --git a/frontend/components/FileUploader/FileUploader.stories.tsx b/frontend/components/FileUploader/FileUploader.stories.tsx index f35ea77122..68de42c15d 100644 --- a/frontend/components/FileUploader/FileUploader.stories.tsx +++ b/frontend/components/FileUploader/FileUploader.stories.tsx @@ -12,7 +12,7 @@ const meta: Meta = { accept: ".pdf", isLoading: false, onFileUpload: () => { - console.log("File uploaded!"); + alert("File uploaded!"); }, }, }; diff --git a/frontend/components/FlashMessage/FlashMessage.tsx b/frontend/components/FlashMessage/FlashMessage.tsx index bef7fb8292..102b2c3601 100644 --- a/frontend/components/FlashMessage/FlashMessage.tsx +++ b/frontend/components/FlashMessage/FlashMessage.tsx @@ -58,14 +58,7 @@ const FlashMessage = ({ } return undefined; // No cleanup when we don't set a timeout. - }, [ - notification, - alertType, - isVisible, - setHide, - isPersistent, - onRemoveFlash, - ]); + }, [notification, alertType, isVisible, setHide]); if (hide || !isVisible) { return null; diff --git a/frontend/components/FleetAce/FleetAce.tsx b/frontend/components/FleetAce/FleetAce.tsx index 25d8856e8a..5a6f6ba7bc 100644 --- a/frontend/components/FleetAce/FleetAce.tsx +++ b/frontend/components/FleetAce/FleetAce.tsx @@ -198,7 +198,7 @@ const FleetAce = ({ onLoad && onLoad(editor); }; - const onBlurHandler = (event: unknown, editor?: IAceEditor): void => { + const onBlurHandler = (event: any, editor?: IAceEditor): void => { onBlur && onBlur(editor); }; From 1d80aa7668ddb44b482205ebd6038f043381e136 Mon Sep 17 00:00:00 2001 From: Rachael Shaw Date: Wed, 3 Apr 2024 11:48:22 -0500 Subject: [PATCH 15/60] When flagging a parameter as premium-only, always include a period. (#18038) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For better readability: add periods as separation between the Fleet Premium message and parameter descriptions. βœ… _Available in Fleet Premium_. Description text. ❌ _Available in Fleet Premium_ Description text. --- docs/REST API/rest-api.md | 60 +++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/docs/REST API/rest-api.md b/docs/REST API/rest-api.md index 39ea5b88c1..68219d49d7 100644 --- a/docs/REST API/rest-api.md +++ b/docs/REST API/rest-api.md @@ -1509,7 +1509,7 @@ Delete all of a team's existing enroll secrets | email | string | body | **Required.** The email of the invited user. This email will receive the invitation link. | | name | string | body | **Required.** The name of the invited user. | | sso_enabled | boolean | body | **Required.** Whether or not SSO will be enabled for the invited user. | -| teams | list | body | _Available in Fleet Premium_ A list of the teams the user is a member of. Each item includes the team's ID and the user's role in the specified team. | +| teams | list | body | _Available in Fleet Premium_. A list of the teams the user is a member of. Each item includes the team's ID and the user's role in the specified team. | #### Example @@ -1709,7 +1709,7 @@ Verify the specified invite. | email | string | body | The email of the invited user. Updates on the email won't resend the invitation. | | name | string | body | The name of the invited user. | | sso_enabled | boolean | body | Whether or not SSO will be enabled for the invited user. | -| teams | list | body | _Available in Fleet Premium_ A list of the teams the user is a member of. Each item includes the team's ID and the user's role in the specified team. | +| teams | list | body | _Available in Fleet Premium_. A list of the teams the user is a member of. Each item includes the team's ID and the user's role in the specified team. | #### Example @@ -1896,10 +1896,10 @@ the `software` table. | mdm_enrollment_status | string | query | The _mobile device management_ (MDM) enrollment status to filter hosts by. Valid options are 'manual', 'automatic', 'enrolled', 'pending', or 'unenrolled'. | | macos_settings | string | query | Filters the hosts by the status of the _mobile device management_ (MDM) profiles applied to hosts. Valid options are 'verified', 'verifying', 'pending', or 'failed'. **Note: If this filter is used in Fleet Premium without a team ID filter, the results include only hosts that are not assigned to any team.** | | munki_issue_id | integer | query | The ID of the _munki issue_ (a Munki-reported error or warning message) to filter hosts by (that is, filter hosts that are affected by that corresponding error or warning message). | -| low_disk_space | integer | query | _Available in Fleet Premium_ Filters the hosts to only include hosts with less GB of disk space available than this value. Must be a number between 1-100. | +| low_disk_space | integer | query | _Available in Fleet Premium_. Filters the hosts to only include hosts with less GB of disk space available than this value. Must be a number between 1-100. | | disable_failing_policies| boolean | query | If `true`, hosts will return failing policies as 0 regardless of whether there are any that failed for the host. This is meant to be used when increased performance is needed in exchange for the extra information. | | macos_settings_disk_encryption | string | query | Filters the hosts by the status of the macOS disk encryption MDM profile on the host. Valid options are 'verified', 'verifying', 'action_required', 'enforcing', 'failed', or 'removing_enforcement'. | -| bootstrap_package | string | query | _Available in Fleet Premium_ Filters the hosts by the status of the MDM bootstrap package on the host. Valid options are 'installed', 'pending', or 'failed'. | +| bootstrap_package | string | query | _Available in Fleet Premium_. Filters the hosts by the status of the MDM bootstrap package on the host. Valid options are 'installed', 'pending', or 'failed'. | | os_settings | string | query | Filters the hosts by the status of the operating system settings applied to the hosts. Valid options are 'verified', 'verifying', 'pending', or 'failed'. **Note: If this filter is used in Fleet Premium without a team ID filter, the results include only hosts that are not assigned to any team.** | | os_settings_disk_encryption | string | query | Filters the hosts by the status of the disk encryption setting applied to the hosts. Valid options are 'verified', 'verifying', 'action_required', 'enforcing', 'failed', or 'removing_enforcement'. **Note: If this filter is used in Fleet Premium without a team ID filter, the results include only hosts that are not assigned to any team.** | | populate_software | boolean | query | If `true`, the response will include a list of installed software for each host, including vulnerability data. | @@ -2115,7 +2115,7 @@ Response payload with the `munki_issue_id` filter provided: | after | string | query | The value to get results after. This needs `order_key` defined, as that's the column that would be used. | | status | string | query | Indicates the status of the hosts to return. Can either be 'new', 'online', 'offline', 'mia' or 'missing'. | | query | string | query | Search query keywords. Searchable fields include `hostname`, `hardware_serial`, `uuid`, `ipv4` and the hosts' email addresses (only searched if the query looks like an email address, i.e. contains an '@', no space, etc.). | -| team_id | integer | query | _Available in Fleet Premium_ Filters the hosts to only include hosts in the specified team. | +| team_id | integer | query | _Available in Fleet Premium_. Filters the hosts to only include hosts in the specified team. | | policy_id | integer | query | The ID of the policy to filter hosts by. | | policy_response | string | query | **Requires `policy_id`**. Valid options are 'passing' or 'failing'. | | software_version_id | integer | query | The ID of the software version to filter hosts by. | @@ -2130,9 +2130,9 @@ Response payload with the `munki_issue_id` filter provided: | mdm_enrollment_status | string | query | The _mobile device management_ (MDM) enrollment status to filter hosts by. Valid options are 'manual', 'automatic', 'enrolled', 'pending', or 'unenrolled'. | | macos_settings | string | query | Filters the hosts by the status of the _mobile device management_ (MDM) profiles applied to hosts. Valid options are 'verified', 'verifying', 'pending', or 'failed'. **Note: If this filter is used in Fleet Premium without a team ID filter, the results include only hosts that are not assigned to any team.** | | munki_issue_id | integer | query | The ID of the _munki issue_ (a Munki-reported error or warning message) to filter hosts by (that is, filter hosts that are affected by that corresponding error or warning message). | -| low_disk_space | integer | query | _Available in Fleet Premium_ Filters the hosts to only include hosts with less GB of disk space available than this value. Must be a number between 1-100. | +| low_disk_space | integer | query | _Available in Fleet Premium_. Filters the hosts to only include hosts with less GB of disk space available than this value. Must be a number between 1-100. | | macos_settings_disk_encryption | string | query | Filters the hosts by the status of the macOS disk encryption MDM profile on the host. Valid options are 'verified', 'verifying', 'action_required', 'enforcing', 'failed', or 'removing_enforcement'. | -| bootstrap_package | string | query | _Available in Fleet Premium_ Filters the hosts by the status of the MDM bootstrap package on the host. Valid options are 'installed', 'pending', or 'failed'. **Note: If this filter is used in Fleet Premium without a team ID filter, the results include only hosts that are not assigned to any team.** | +| bootstrap_package | string | query | _Available in Fleet Premium_. Filters the hosts by the status of the MDM bootstrap package on the host. Valid options are 'installed', 'pending', or 'failed'. **Note: If this filter is used in Fleet Premium without a team ID filter, the results include only hosts that are not assigned to any team.** | | os_settings | string | query | Filters the hosts by the status of the operating system settings applied to the hosts. Valid options are 'verified', 'verifying', 'pending', or 'failed'. **Note: If this filter is used in Fleet Premium without a team ID filter, the results include only hosts that are not assigned to any team.** | | os_settings_disk_encryption | string | query | Filters the hosts by the status of the disk encryption setting applied to the hosts. Valid options are 'verified', 'verifying', 'action_required', 'enforcing', 'failed', or 'removing_enforcement'. **Note: If this filter is used in Fleet Premium without a team ID filter, the results include only hosts that are not assigned to any team.** | @@ -3289,7 +3289,7 @@ Retrieves MDM enrollment summary. Windows servers are excluded from the aggregat | Name | Type | In | Description | | -------- | ------- | ----- | -------------------------------------------------------------------------------- | -| team_id | integer | query | _Available in Fleet Premium_ Filter by team | +| team_id | integer | query | _Available in Fleet Premium_. Filter by team | | platform | string | query | Filter by platform ("windows" or "darwin") | A `team_id` of `0` returns the statistics for hosts that are not part of any team. A `null` or missing `team_id` returns statistics for all hosts regardless of the team. @@ -3399,7 +3399,7 @@ Retrieves aggregated host's MDM enrollment status and Munki versions. | Name | Type | In | Description | | ------- | ------- | ----- | ---------------------------------------------------------------------------------------------------------------- | -| team_id | integer | query | _Available in Fleet Premium_ Filters the aggregate host information to only include hosts in the specified team. | | +| team_id | integer | query | _Available in Fleet Premium_. Filters the aggregate host information to only include hosts in the specified team. | | A `team_id` of `0` returns the statistics for hosts that are not part of any team. A `null` or missing `team_id` returns statistics for all hosts regardless of the team. @@ -3680,7 +3680,7 @@ requested by a web browser. | order_direction | string | query | **Requires `order_key`**. The direction of the order given the order key. Options include 'asc' and 'desc'. Default is 'asc'. | | status | string | query | Indicates the status of the hosts to return. Can either be 'new', 'online', 'offline', 'mia' or 'missing'. | | query | string | query | Search query keywords. Searchable fields include `hostname`, `hardware_serial`, `uuid`, `ipv4` and the hosts' email addresses (only searched if the query looks like an email address, i.e. contains an `@`, no space, etc.). | -| team_id | integer | query | _Available in Fleet Premium_ Filters the hosts to only include hosts in the specified team. | +| team_id | integer | query | _Available in Fleet Premium_. Filters the hosts to only include hosts in the specified team. | | policy_id | integer | query | The ID of the policy to filter hosts by. | | policy_response | string | query | **Requires `policy_id`**. Valid options are 'passing' or 'failing'. **Note: If `policy_id` is specified _without_ including `policy_response`, this will also return hosts where the policy is not configured to run or failed to run.** | | software_version_id | integer | query | The ID of the software version to filter hosts by. | @@ -3694,9 +3694,9 @@ requested by a web browser. | mdm_enrollment_status | string | query | The _mobile device management_ (MDM) enrollment status to filter hosts by. Valid options are 'manual', 'automatic', 'enrolled', 'pending', or 'unenrolled'. | | macos_settings | string | query | Filters the hosts by the status of the _mobile device management_ (MDM) profiles applied to hosts. Valid options are 'verified', 'verifying', 'pending', or 'failed'. **Note: If this filter is used in Fleet Premium without a team ID filter, the results include only hosts that are not assigned to any team.** | | munki_issue_id | integer | query | The ID of the _munki issue_ (a Munki-reported error or warning message) to filter hosts by (that is, filter hosts that are affected by that corresponding error or warning message). | -| low_disk_space | integer | query | _Available in Fleet Premium_ Filters the hosts to only include hosts with less GB of disk space available than this value. Must be a number between 1-100. | +| low_disk_space | integer | query | _Available in Fleet Premium_. Filters the hosts to only include hosts with less GB of disk space available than this value. Must be a number between 1-100. | | label_id | integer | query | A valid label ID. Can only be used in combination with `order_key`, `order_direction`, `status`, `query` and `team_id`. | -| bootstrap_package | string | query | _Available in Fleet Premium_ Filters the hosts by the status of the MDM bootstrap package on the host. Valid options are 'installed', 'pending', or 'failed'. **Note: If this filter is used in Fleet Premium without a team ID filter, the results include only hosts that are not assigned to any team.** | +| bootstrap_package | string | query | _Available in Fleet Premium_. Filters the hosts by the status of the MDM bootstrap package on the host. Valid options are 'installed', 'pending', or 'failed'. **Note: If this filter is used in Fleet Premium without a team ID filter, the results include only hosts that are not assigned to any team.** | | disable_failing_policies | boolean | query | If `true`, hosts will return failing policies as 0 (returned as the `issues` column) regardless of whether there are any that failed for the host. This is meant to be used when increased performance is needed in exchange for the extra information. | If `mdm_id`, `mdm_name` or `mdm_enrollment_status` is specified, then Windows Servers are excluded from the results. @@ -4424,15 +4424,15 @@ Returns a list of the hosts that belong to the specified label. | after | string | query | The value to get results after. This needs `order_key` defined, as that's the column that would be used. | | status | string | query | Indicates the status of the hosts to return. Can either be 'new', 'online', 'offline', 'mia' or 'missing'. | | query | string | query | Search query keywords. Searchable fields include `hostname`, `hardware_serial`, `uuid`, and `ipv4`. | -| team_id | integer | query | _Available in Fleet Premium_ Filters the hosts to only include hosts in the specified team. | +| team_id | integer | query | _Available in Fleet Premium_. Filters the hosts to only include hosts in the specified team. | | disable_failing_policies | boolean | query | If "true", hosts will return failing policies as 0 regardless of whether there are any that failed for the host. This is meant to be used when increased performance is needed in exchange for the extra information. | | mdm_id | integer | query | The ID of the _mobile device management_ (MDM) solution to filter hosts by (that is, filter hosts that use a specific MDM provider and URL). | | mdm_name | string | query | The name of the _mobile device management_ (MDM) solution to filter hosts by (that is, filter hosts that use a specific MDM provider). | | mdm_enrollment_status | string | query | The _mobile device management_ (MDM) enrollment status to filter hosts by. Valid options are 'manual', 'automatic', 'enrolled', 'pending', or 'unenrolled'. | | macos_settings | string | query | Filters the hosts by the status of the _mobile device management_ (MDM) profiles applied to hosts. Valid options are 'verified', 'verifying', 'pending', or 'failed'. **Note: If this filter is used in Fleet Premium without a team ID filter, the results include only hosts that are not assigned to any team.** | -| low_disk_space | integer | query | _Available in Fleet Premium_ Filters the hosts to only include hosts with less GB of disk space available than this value. Must be a number between 1-100. | +| low_disk_space | integer | query | _Available in Fleet Premium_. Filters the hosts to only include hosts with less GB of disk space available than this value. Must be a number between 1-100. | | macos_settings_disk_encryption | string | query | Filters the hosts by the status of the macOS disk encryption MDM profile on the host. Valid options are 'verified', 'verifying', 'action_required', 'enforcing', 'failed', or 'removing_enforcement'. | -| bootstrap_package | string | query | _Available in Fleet Premium_ Filters the hosts by the status of the MDM bootstrap package on the host. Valid options are 'installed', 'pending', or 'failed'. **Note: If this filter is used in Fleet Premium without a team ID filter, the results include only hosts that are not assigned to any team.** | +| bootstrap_package | string | query | _Available in Fleet Premium_. Filters the hosts by the status of the MDM bootstrap package on the host. Valid options are 'installed', 'pending', or 'failed'. **Note: If this filter is used in Fleet Premium without a team ID filter, the results include only hosts that are not assigned to any team.** | | os_settings | string | query | Filters the hosts by the status of the operating system settings applied to the hosts. Valid options are 'verified', 'verifying', 'pending', or 'failed'. **Note: If this filter is used in Fleet Premium without a team ID filter, the results include only hosts that are not assigned to any team.** | | os_settings_disk_encryption | string | query | Filters the hosts by the status of the disk encryption setting applied to the hosts. Valid options are 'verified', 'verifying', 'action_required', 'enforcing', 'failed', or 'removing_enforcement'. **Note: If this filter is used in Fleet Premium without a team ID filter, the results include only hosts that are not assigned to any team.** | @@ -4589,8 +4589,8 @@ Add a configuration profile to enforce custom settings on macOS and Windows host | Name | Type | In | Description | | ------------------------- | -------- | ---- | ------------------------------------------------------------------------------------------------------------- | | profile | file | form | **Required.** The .mobileconfig (macOS) or XML (Windows) file containing the profile. | -| team_id | string | form | _Available in Fleet Premium_ The team ID for the profile. If specified, the profile is applied to only hosts that are assigned to the specified team. If not specified, the profile is applied to only to hosts that are not assigned to any team. | -| labels | array | form | _Available in Fleet Premium_ An array of labels to filter hosts in a team (or no team) that should get a profile. | +| team_id | string | form | _Available in Fleet Premium_. The team ID for the profile. If specified, the profile is applied to only hosts that are assigned to the specified team. If not specified, the profile is applied to only to hosts that are not assigned to any team. | +| labels | array | form | _Available in Fleet Premium_. An array of labels to filter hosts in a team (or no team) that should get a profile. | #### Example @@ -4700,7 +4700,7 @@ results (i.e., only profiles that are associated with "No team" are listed). | Name | Type | In | Description | | ------------------------- | ------ | ----- | ------------------------------------------------------------------------- | -| team_id | string | query | _Available in Fleet Premium_ The team id to filter profiles. | +| team_id | string | query | _Available in Fleet Premium_. The team id to filter profiles. | | page | integer | query | Page number of the results to fetch. | | per_page | integer | query | Results per page. | @@ -4900,7 +4900,7 @@ The summary can optionally be filtered by team ID. | Name | Type | In | Description | | ------------------------- | ------ | ----- | ------------------------------------------------------------------------- | -| team_id | string | query | _Available in Fleet Premium_ The team ID to filter the summary. | +| team_id | string | query | _Available in Fleet Premium_. The team ID to filter the summary. | #### Example @@ -4938,7 +4938,7 @@ optionally be filtered by `team_id`. If no `team_id` is specified, team profiles | Name | Type | In | Description | | ------------------------- | ------ | ----- | ------------------------------------------------------------------------- | -| team_id | string | query | _Available in Fleet Premium_ The team ID to filter profiles. | +| team_id | string | query | _Available in Fleet Premium_. The team ID to filter profiles. | #### Example @@ -5874,7 +5874,7 @@ Where `query_id` references an existing `query`. | description | string | body | The query's description. | | resolution | string | body | The resolution steps for the policy. | | platform | string | body | Comma-separated target platforms, currently supported values are "windows", "linux", "darwin". The default, an empty string means target all platforms. | -| critical | boolean | body | _Available in Fleet Premium_ Mark policy as critical/high impact. | +| critical | boolean | body | _Available in Fleet Premium_. Mark policy as critical/high impact. | #### Example @@ -6137,7 +6137,7 @@ The semantics for creating a team policy are the same as for global policies, se | resolution | string | body | The resolution steps for the policy. | | query_id | integer | body | An existing query's ID (legacy). | | platform | string | body | Comma-separated target platforms, currently supported values are "windows", "linux", "darwin". The default, an empty string means target all platforms. | -| critical | boolean | body | _Available in Fleet Premium_ Mark policy as critical/high impact. | +| critical | boolean | body | _Available in Fleet Premium_. Mark policy as critical/high impact. | Either `query` or `query_id` must be provided. @@ -6233,7 +6233,7 @@ Either `query` or `query_id` must be provided. | description | string | body | The query's description. | | resolution | string | body | The resolution steps for the policy. | | platform | string | body | Comma-separated target platforms, currently supported values are "windows", "linux", "darwin". The default, an empty string means target all platforms. | -| critical | boolean | body | _Available in Fleet Premium_ Mark policy as critical/high impact. | +| critical | boolean | body | _Available in Fleet Premium_. Mark policy as critical/high impact. | #### Example @@ -7732,7 +7732,7 @@ Get a list of all software. | order_key | string | query | What to order results by. Allowed fields are `name` and `hosts_count`. Default is `hosts_count` (descending). | | order_direction | string | query | **Requires `order_key`**. The direction of the order given the order key. Options include `asc` and `desc`. Default is `asc`. | | query | string | query | Search query keywords. Searchable fields include `title` and `cve`. | -| team_id | integer | query | _Available in Fleet Premium_ Filters the software to only include the software installed on the hosts that are assigned to the specified team. | +| team_id | integer | query | _Available in Fleet Premium_. Filters the software to only include the software installed on the hosts that are assigned to the specified team. | | vulnerable | bool | query | If true or 1, only list software that has detected vulnerabilities. Default is `false`. | #### Example @@ -7841,7 +7841,7 @@ Get a list of all software versions. | order_key | string | query | What to order results by. Allowed fields are `name`, `hosts_count`, `cve_published`, `cvss_score`, `epss_probability` and `cisa_known_exploit`. Default is `hosts_count` (descending). | | order_direction | string | query | **Requires `order_key`**. The direction of the order given the order key. Options include `asc` and `desc`. Default is `asc`. | | query | string | query | Search query keywords. Searchable fields include `name`, `version`, and `cve`. | -| team_id | integer | query | _Available in Fleet Premium_ Filters the software to only include the software installed on the hosts that are assigned to the specified team. | +| team_id | integer | query | _Available in Fleet Premium_. Filters the software to only include the software installed on the hosts that are assigned to the specified team. | | vulnerable | bool | query | If true or 1, only list software that has detected vulnerabilities. Default is `false`. | #### Example @@ -8024,7 +8024,7 @@ Retrieves a list of all CVEs affecting software and/or OS versions. | Name | Type | In | Description | | --- | --- | --- | --- | -| team_id | integer | query | _Available in Fleet Premium_ Filters only include vulnerabilities affecting the specified team. | +| team_id | integer | query | _Available in Fleet Premium_. Filters only include vulnerabilities affecting the specified team. | | page | integer | query | Page number of the results to fetch. | | per_page | integer | query | Results per page. | | order_key | string | query | What to order results by. Allowed fields are: `cve`, `cvss_score`, `epss_probability`, `cve_published`, `created_at`, and `host_count`. Default is `created_at` (descending). | @@ -9027,7 +9027,7 @@ Returns a list of all enabled users | page | integer | query | Page number of the results to fetch. | | query | string | query | Search query keywords. Searchable fields include `name` and `email`. | | per_page | integer | query | Results per page. | -| team_id | integer | query | _Available in Fleet Premium_ Filters the users to only include users in the specified team. | +| team_id | integer | query | _Available in Fleet Premium_. Filters the users to only include users in the specified team. | #### Example @@ -9101,7 +9101,7 @@ Creates a user account after an invited user provides registration information a | password | string | body | The password chosen by the user (if not SSO user). | | password_confirmation | string | body | Confirmation of the password chosen by the user. | | global_role | string | body | The role assigned to the user. In Fleet 4.0.0, 3 user roles were introduced (`admin`, `maintainer`, and `observer`). In Fleet 4.30.0 and 4.31.0, the `observer_plus` and `gitops` roles were introduced respectively. If `global_role` is specified, `teams` cannot be specified. For more information, see [manage access](https://fleetdm.com/docs/using-fleet/manage-access). | -| teams | array | body | _Available in Fleet Premium_ The teams and respective roles assigned to the user. Should contain an array of objects in which each object includes the team's `id` and the user's `role` on each team. In Fleet 4.0.0, 3 user roles were introduced (`admin`, `maintainer`, and `observer`). In Fleet 4.30.0 and 4.31.0, the `observer_plus` and `gitops` roles were introduced respectively. If `teams` is specified, `global_role` cannot be specified. For more information, see [manage access](https://fleetdm.com/docs/using-fleet/manage-access). | +| teams | array | body | _Available in Fleet Premium_. The teams and respective roles assigned to the user. Should contain an array of objects in which each object includes the team's `id` and the user's `role` on each team. In Fleet 4.0.0, 3 user roles were introduced (`admin`, `maintainer`, and `observer`). In Fleet 4.30.0 and 4.31.0, the `observer_plus` and `gitops` roles were introduced respectively. If `teams` is specified, `global_role` cannot be specified. For more information, see [manage access](https://fleetdm.com/docs/using-fleet/manage-access). | #### Example @@ -9219,7 +9219,7 @@ By default, the user will be forced to reset its password upon first login. | api_only | boolean | body | User is an "API-only" user (cannot use web UI) if true. | | global_role | string | body | The role assigned to the user. In Fleet 4.0.0, 3 user roles were introduced (`admin`, `maintainer`, and `observer`). In Fleet 4.30.0 and 4.31.0, the `observer_plus` and `gitops` roles were introduced respectively. If `global_role` is specified, `teams` cannot be specified. For more information, see [manage access](https://fleetdm.com/docs/using-fleet/manage-access). | | admin_forced_password_reset | boolean | body | Sets whether the user will be forced to reset its password upon first login (default=true) | -| teams | array | body | _Available in Fleet Premium_ The teams and respective roles assigned to the user. Should contain an array of objects in which each object includes the team's `id` and the user's `role` on each team. In Fleet 4.0.0, 3 user roles were introduced (`admin`, `maintainer`, and `observer`). In Fleet 4.30.0 and 4.31.0, the `observer_plus` and `gitops` roles were introduced respectively. If `teams` is specified, `global_role` cannot be specified. For more information, see [manage access](https://fleetdm.com/docs/using-fleet/manage-access). | +| teams | array | body | _Available in Fleet Premium_. The teams and respective roles assigned to the user. Should contain an array of objects in which each object includes the team's `id` and the user's `role` on each team. In Fleet 4.0.0, 3 user roles were introduced (`admin`, `maintainer`, and `observer`). In Fleet 4.30.0 and 4.31.0, the `observer_plus` and `gitops` roles were introduced respectively. If `teams` is specified, `global_role` cannot be specified. For more information, see [manage access](https://fleetdm.com/docs/using-fleet/manage-access). | #### Example @@ -9372,7 +9372,7 @@ Returns all information about a specific user. | password | string | body | The user's current password, required to change the user's own email or password (not required for an admin to modify another user). | | new_password| string | body | The user's new password. | | global_role | string | body | The role assigned to the user. In Fleet 4.0.0, 3 user roles were introduced (`admin`, `maintainer`, and `observer`). If `global_role` is specified, `teams` cannot be specified. | -| teams | array | body | _Available in Fleet Premium_ The teams and respective roles assigned to the user. Should contain an array of objects in which each object includes the team's `id` and the user's `role` on each team. In Fleet 4.0.0, 3 user roles were introduced (`admin`, `maintainer`, and `observer`). If `teams` is specified, `global_role` cannot be specified. | +| teams | array | body | _Available in Fleet Premium_. The teams and respective roles assigned to the user. Should contain an array of objects in which each object includes the team's `id` and the user's `role` on each team. In Fleet 4.0.0, 3 user roles were introduced (`admin`, `maintainer`, and `observer`). If `teams` is specified, `global_role` cannot be specified. | #### Example From 746309ca47593db5c75d4e953785094be2fc9cd9 Mon Sep 17 00:00:00 2001 From: Victor Lyuboslavsky Date: Wed, 3 Apr 2024 16:04:01 -0500 Subject: [PATCH 16/60] Waive 3 Go code scanning vulnerability alerts. (#18007) Waiving 3 Go code scanning alerts flagged by osv-scanner. --- osv-scanner.toml | 10 ++++++++++ terraform/addons/monitoring/lambda/osv-scanner.toml | 6 ++++++ tools/blackhat-mdm/mdm_server_poc/osv-scanner.toml | 6 ++++++ 3 files changed, 22 insertions(+) create mode 100644 osv-scanner.toml create mode 100644 terraform/addons/monitoring/lambda/osv-scanner.toml create mode 100644 tools/blackhat-mdm/mdm_server_poc/osv-scanner.toml diff --git a/osv-scanner.toml b/osv-scanner.toml new file mode 100644 index 0000000000..a8f2449ff7 --- /dev/null +++ b/osv-scanner.toml @@ -0,0 +1,10 @@ +# Configure OSV-Scanner +# https://google.github.io/osv-scanner/configuration/ + +[[IgnoredVulns]] +id = "GO-2022-0646" +reason = "2024/04/02 - This project does not use github.com/aws/aws-sdk-go/service/s3/s3crypto. Reference: https://osv.dev/vulnerability/GO-2022-0646" + +[[IgnoredVulns]] +id = "GO-2023-1788" +reason = "2024/04/02 - When packaging linux files, we do not use global permissions. Manually verified that packed fleet-osquery files do not have group/global write permissions. Reference: https://osv.dev/vulnerability/GO-2023-1788" diff --git a/terraform/addons/monitoring/lambda/osv-scanner.toml b/terraform/addons/monitoring/lambda/osv-scanner.toml new file mode 100644 index 0000000000..e1bce5d2de --- /dev/null +++ b/terraform/addons/monitoring/lambda/osv-scanner.toml @@ -0,0 +1,6 @@ +# Configure OSV-Scanner +# https://google.github.io/osv-scanner/configuration/ + +[[IgnoredVulns]] +id = "GO-2022-0646" +reason = "2024/04/02 - This project does not use github.com/aws/aws-sdk-go/service/s3/s3crypto. Reference: https://osv.dev/vulnerability/GO-2022-0646" diff --git a/tools/blackhat-mdm/mdm_server_poc/osv-scanner.toml b/tools/blackhat-mdm/mdm_server_poc/osv-scanner.toml new file mode 100644 index 0000000000..0ece8d3378 --- /dev/null +++ b/tools/blackhat-mdm/mdm_server_poc/osv-scanner.toml @@ -0,0 +1,6 @@ +# Configure OSV-Scanner +# https://google.github.io/osv-scanner/configuration/ + +[[IgnoredVulns]] +id = "GO-2023-2402" +reason = "2024/04/02 - This is not production code." From f8157ce679358dd825fa9836580fafd16630e129 Mon Sep 17 00:00:00 2001 From: Mike McNeil Date: Wed, 3 Apr 2024 19:40:15 -0500 Subject: [PATCH 17/60] Website: personalize contact form (#18026) Co-authored-by: Eric --- website/assets/js/pages/contact.page.js | 4 ++-- website/views/pages/contact.ejs | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/website/assets/js/pages/contact.page.js b/website/assets/js/pages/contact.page.js index 597b7bd8ce..e5132f4fb8 100644 --- a/website/assets/js/pages/contact.page.js +++ b/website/assets/js/pages/contact.page.js @@ -4,7 +4,7 @@ parasails.registerPage('contact', { // β•©β•β•šβ•β•© β•© β•©β•© ╩╩═╝ β•šβ•β• β•© β•© β•© β•© β•šβ•β• data: { formToDisplay: 'talk-to-us', - audience: undefined, + primaryBuyingSituation: undefined, // Main syncing/loading state for this page. syncing: false, @@ -50,7 +50,7 @@ parasails.registerPage('contact', { window.history.replaceState({}, document.title, '/contact' ); } if(this.primaryBuyingSituation){ - this.audience = this.primaryBuyingSituation; + this.formData.primaryBuyingSituation = this.primaryBuyingSituation;// prefill form } }, mounted: async function() { diff --git a/website/views/pages/contact.ejs b/website/views/pages/contact.ejs index d7bcf17d6a..0982fc06eb 100644 --- a/website/views/pages/contact.ejs +++ b/website/views/pages/contact.ejs @@ -88,10 +88,10 @@
Please select an option.
- +

Includes computers, servers, containers, and other hosts.

-
Please enter a number of devices
+
Please enter a number of <%= primaryBuyingSituation === 'mdm' ? 'devices' : 'hosts' %>

Please enter a valid work email address

@@ -108,7 +108,7 @@

A member of our team will get back to you soon.
Usually within one business day (or less!)

-
+
Deloitte logo

Something I really appreciate about working with you guys is that it doesn't feel like I'm talking to a vendor. It actually feels like I'm talking to my team, and I really appreciate it. @@ -123,7 +123,7 @@

-
+
Uber logo

Exciting. This is a team that listens to feedback. @@ -138,7 +138,7 @@

-
+

The visibility down into the assets covered by the agent is phenomenal. Fleet has become the central source for a lot of things.

@@ -152,7 +152,7 @@
-
+
Deputy logo

When we look at vendors, we look for ones that are very receptive to feedback, where you’re just part of the family, I guess. Fleet’s really good at that. @@ -167,7 +167,7 @@

-
+
Atlassian logo

I love the steady and consistent delivery of features that help teams work how they want to work, not how your product dictates they work. From a5c0224f42149a222f4f3f32cd029ff8901c55a0 Mon Sep 17 00:00:00 2001 From: Joanne Stableford <59930035+JoStableford@users.noreply.github.com> Date: Thu, 4 Apr 2024 03:07:58 -0400 Subject: [PATCH 18/60] Add process for reporting customer revenue status (#17965) ... --------- Co-authored-by: Sam Pfluger <108141731+Sampfluger88@users.noreply.github.com> --- handbook/business-operations/README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/handbook/business-operations/README.md b/handbook/business-operations/README.md index 32ad5d2006..4026031412 100644 --- a/handbook/business-operations/README.md +++ b/handbook/business-operations/README.md @@ -125,6 +125,18 @@ Certain new team members, especially in go-to-market (GTM) roles, will need paid > **Warning:** Do NOT buy LinkedIn Recruiter. AEs and SDRs should use their personal Brex card to purchase the monthly [Core Sales Navigator](https://business.linkedin.com/sales-solutions/compare-plans) plan. Fleet does not use a company wide Sales Navigator account. The goal of Sales Navigator is to access to profile views and data, not InMail. Fleet does not send InMail. +### Communicate the status of customer financial actions +This reporting is performed to update the status of open or upcoming customer actions regarding the financial health of the opportunity. To complete the report: +- Go to this [report folder](https://fleetdm.lightning.force.com/lightning/r/Folder/00lUG000000DstpYAC/view?queryScope=userFolders) in SFDC. The three reports will provide the data used in the report. +- Copy the template below and paste it into the [#g-sales slack channel](https://fleetdm.slack.com/archives/C030A767HQV) and complete all "todos" using the data from Salesforce before sending. +``` +Weekly revenue report - [@`todo: CRO` and @`todo: CEO`] +- Number accounts with outstanding balances = `todo` +- Number of customers awaiting invoices = `todo` +- Number of past-due renewals = `todo` +``` + + ### Add a seat to Salesforce Here are the steps we take to grant appropriate Salesforce licenses to a new hire: - Go to ["My Account"](https://fleetdm.lightning.force.com/lightning/n/standard-OnlineSalesHome). From 3de9a160ed7a9fa13b7fe61564736a61c0001f56 Mon Sep 17 00:00:00 2001 From: Joanne Stableford <59930035+JoStableford@users.noreply.github.com> Date: Thu, 4 Apr 2024 03:24:29 -0400 Subject: [PATCH 19/60] Add info on how Fleeties can update benefit coverage for QLEs (#17998) ...Adding to our institutional knowledge. --- handbook/business-operations/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/handbook/business-operations/README.md b/handbook/business-operations/README.md index 4026031412..4fc8c9bc5a 100644 --- a/handbook/business-operations/README.md +++ b/handbook/business-operations/README.md @@ -182,6 +182,7 @@ When a Fleetie, consultant or advisor requests an update to their personnel deta - If required, BizOps also makes changes to other core systems (e.g: creating a new email alias in google workspace; updating details in Carta; etc). - The change is now actioned, notify the team member and close the issue. +> Note: if the Fleetie is US based and has a qualifying life event that impacts benefit coverage, they can [follow the Gusto steps](https://support.gusto.com/article/100895878100000/Change-your-benefits-with-a-qualifying-life-event) to update their coverage elections. ### Change a Fleetie's job title When BizOps receives notification of a Fleetie's job title changing, follow these steps to ensure accurate recording of the change across our systems. From 5a3c54c0b6dfd5c3b46e5e667fb5ffc99885b29b Mon Sep 17 00:00:00 2001 From: Victor Lyuboslavsky Date: Thu, 4 Apr 2024 09:33:17 -0500 Subject: [PATCH 20/60] Updated 1Password policy to only search 1 level deep for performance reasons. (#18003) #17827 Updated 1Password policy to only search one level deep for performance reasons. --------- Co-authored-by: Noah Talerman <47070608+noahtalerman@users.noreply.github.com> --- .../standard-query-library/standard-query-library.yml | 6 +++--- it-and-security/lib/macos-device-health.policies.yml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/01-Using-Fleet/standard-query-library/standard-query-library.yml b/docs/01-Using-Fleet/standard-query-library/standard-query-library.yml index 4e451247b6..e9c073fa0c 100644 --- a/docs/01-Using-Fleet/standard-query-library/standard-query-library.yml +++ b/docs/01-Using-Fleet/standard-query-library/standard-query-library.yml @@ -879,9 +879,9 @@ spec: apiVersion: v1 kind: policy spec: - name: No 1Password emergency kit stored on desktop or in downloads (macOS) - query: SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM file WHERE filename LIKE '%Emergency Kit%.pdf' AND (path LIKE '/Users/%%/Desktop/%%' OR path LIKE '/Users/%%/Documents/%%' OR path LIKE '/Users/%%/Downloads/%%' OR path LIKE '/Users/Shared')); - description: "Looks for PDF files with file names typically used by 1Password for emergency recovery kits." + name: No 1Password emergency kit stored in desktop, documents, or downloads folders (macOS) + query: SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM file WHERE filename LIKE '%Emergency Kit%.pdf' AND (path LIKE '/Users/%/Desktop/%' OR path LIKE '/Users/%/Documents/%' OR path LIKE '/Users/%/Downloads/%' OR path LIKE '/Users/Shared/%')); + description: "Looks for PDF files with file names typically used by 1Password for emergency recovery kits. To protect the performance of your devices, the search is one level deep and limited to the Desktop, Documents, Downloads, and Shared folders." resolution: "Delete 1Password emergency kits from your computer, and empty the trash. 1Password emergency kits should only be printed and stored in a physically secure location." platform: darwin tags: compliance, built-in diff --git a/it-and-security/lib/macos-device-health.policies.yml b/it-and-security/lib/macos-device-health.policies.yml index ea00f2baa6..b706cd80c1 100644 --- a/it-and-security/lib/macos-device-health.policies.yml +++ b/it-and-security/lib/macos-device-health.policies.yml @@ -53,9 +53,9 @@ description: This policy checks if maximum amount of time (in minutes) the device is allowed to sit idle before the screen is locked. End users can select any value less than the specified maximum. resolution: An an IT admin, deploy a macOS, screen saver profile with the maxInactivity option set to 20 minutes. platform: darwin -- name: macOS - No 1Password emergency kit stored on desktop or in downloads - query: SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM file WHERE filename LIKE '%Emergency Kit%.pdf' AND (path LIKE '/Users/%%/Desktop/%%' OR path LIKE '/Users/%%/Documents/%%' OR path LIKE '/Users/%%/Downloads/%%' OR path LIKE '/Users/Shared')); +- name: macOS - No 1Password emergency kit stored in desktop, documents, or downloads folders + query: SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM file WHERE filename LIKE '%Emergency Kit%.pdf' AND (path LIKE '/Users/%/Desktop/%' OR path LIKE '/Users/%/Documents/%' OR path LIKE '/Users/%/Downloads/%' OR path LIKE '/Users/Shared/%')); critical: false - description: "Looks for PDF files with file names typically used by 1Password for emergency recovery kits." + description: "Looks for PDF files with file names typically used by 1Password for emergency recovery kits. To protect the performance of your devices, the search is one level deep and limited to the Desktop, Documents, Downloads, and Shared folders." resolution: "Delete 1Password emergency kits from your computer, and empty the trash. 1Password emergency kits should only be printed and stored in a physically secure location." platform: darwin From 80b1f1f78f708af77cd6dd58fbe9e1af4fcff4b5 Mon Sep 17 00:00:00 2001 From: Luke Heath Date: Thu, 4 Apr 2024 10:01:46 -0500 Subject: [PATCH 21/60] Prepare Fleet v4.48.0 (#18022) --- CHANGELOG.md | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9057a35aa5..85ece1a556 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,25 +1,17 @@ -## Fleet 4.48.0 (Apr 02, 2024) - -### Bug fixes - -## Changelog +## Fleet 4.48.0 (Apr 03, 2024) ### Endpoint operations - - Added integration with Google Calendar. - -* Fleet admins can enable Google Calendar integration by using a Google service account with domain-wide delegation. -* Calendar integration is enabled at the team level for specific team policies. -* If the policy is failing, a calendar event will be put on the host user's calendar for the 3rd Tuesday of the month. -* During the event, Fleet will fire a webhook. IT admins should use this webhook to trigger a script or MDM command that will remediate the issue. - + * Fleet admins can enable Google Calendar integration by using a Google service account with domain-wide delegation. + * Calendar integration is enabled at the team level for specific team policies. + * If the policy is failing, a calendar event will be put on the host user's calendar for the 3rd Tuesday of the month. + * During the event, Fleet will fire a webhook. IT admins should use this webhook to trigger a script or MDM command that will remediate the issue. - Reduced the number of 'Deadlock found' errors seen by the server when multiple hosts share the same UUID. - Removed outdated tooltips from UI. - Added hover states to clickable elements. - Added cross-platform check for duplicate MDM profiles names in batch set MDM profiles API. ### Device management (MDM) - - Added Windows MDM support to the `osquery-perf` host-simulation command. - Added a missing database index to the MDM Windows enrollments table that will improve performance at scale. - Migrate MDM-related endpoints to new paths, deprecating (but still supporting indefinitely) the old endpoints. @@ -29,11 +21,9 @@ - Automatically release a macOS DEP-enrolled device after enrollment commands and profiles have been delivered, unless `enable_release_device_manually` is set to `true`. ### Vulnerability management - - Added Visual Studio extensions to Fleet's software inventory. ### Bug fixes - - Fixed a bug where valid MDM enrollments would show up as unmanaged (EnrollmentState 3). - Fixed flash message from closing when a modal closes. - Fixed a bug where OS version information would not get detected on Windows Server 2019. @@ -51,6 +41,7 @@ - Fixed a bug where `null` or excluded `smtp_settings` caused a UI 500. - Fixed query reports so they reset when there is a change to the selected platform or selected minimum osquery version. - Fixed live query sort of sql result sort for both string and numerical columns. + ## Fleet 4.47.3 (Mar 26, 2024) ### Bug fixes From 0db1c225f4da27bfd31d5b458a40e5cda89f8f55 Mon Sep 17 00:00:00 2001 From: RachelElysia <71795832+RachelElysia@users.noreply.github.com> Date: Thu, 4 Apr 2024 11:34:07 -0400 Subject: [PATCH 22/60] Fleet UI: Fix several flows where team ID is not being preserved (#17968) --- changes/17194-fix-edge-cases-team-id-lost | 1 + .../hosts/ManageHostsPage/ManageHostsPage.tsx | 8 +++++++ .../HostDetailsPage/HostDetailsPage.tsx | 6 +++-- .../QueryDetailsPage/QueryDetailsPage.tsx | 24 ++++++++++++++----- frontend/pages/queries/edit/EditQueryPage.tsx | 19 ++++++++++++--- .../EditQueryForm/EditQueryForm.tsx | 7 +++--- .../live/LiveQueryPage/LiveQueryPage.tsx | 6 ++--- frontend/utilities/helpers.tsx | 9 +++++-- 8 files changed, 61 insertions(+), 19 deletions(-) create mode 100644 changes/17194-fix-edge-cases-team-id-lost diff --git a/changes/17194-fix-edge-cases-team-id-lost b/changes/17194-fix-edge-cases-team-id-lost new file mode 100644 index 0000000000..a871a1b04f --- /dev/null +++ b/changes/17194-fix-edge-cases-team-id-lost @@ -0,0 +1 @@ +Fleet UI: Fix edge cases of team ID being lost in various flows \ No newline at end of file diff --git a/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx b/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx index 0956b3413e..c6a1c62ecf 100644 --- a/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx +++ b/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx @@ -137,6 +137,9 @@ const ManageHostsPage = ({ isFreeTier, isSandboxMode, setFilteredHostsPath, + setFilteredPoliciesPath, + setFilteredQueriesPath, + setFilteredSoftwarePath, } = useContext(AppContext); const { renderFlash } = useContext(NotificationContext); @@ -886,6 +889,11 @@ const ManageHostsPage = ({ // tableQueryData) handleTeamChange(teamId); handleResetPageIndex(); + // Must clear other page paths or the team might accidentally switch + // When navigating from host details + setFilteredSoftwarePath(""); + setFilteredQueriesPath(""); + setFilteredPoliciesPath(""); }, [handleTeamChange] ); diff --git a/frontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsx b/frontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsx index 4f03cb495a..dbab4b2288 100644 --- a/frontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsx +++ b/frontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsx @@ -147,6 +147,7 @@ const HostDetailsPage = ({ isSandboxMode, isOnlyObserver, filteredHostsPath, + currentTeam, } = useContext(AppContext); const { setSelectedQueryTargetsByType } = useContext(QueryContext); const { renderFlash } = useContext(NotificationContext); @@ -567,7 +568,8 @@ const HostDetailsPage = ({ const onQueryHostCustom = () => { setSelectedQueryTargetsByType(DEFAULT_TARGETS_BY_TYPE); router.push( - PATHS.NEW_QUERY() + TAGGED_TEMPLATES.queryByHostRoute(host?.id) + PATHS.NEW_QUERY() + + TAGGED_TEMPLATES.queryByHostRoute(host?.id, currentTeam?.id) ); }; @@ -575,7 +577,7 @@ const HostDetailsPage = ({ setSelectedQueryTargetsByType(DEFAULT_TARGETS_BY_TYPE); router.push( PATHS.EDIT_QUERY(selectedQuery.id) + - TAGGED_TEMPLATES.queryByHostRoute(host?.id) + TAGGED_TEMPLATES.queryByHostRoute(host?.id, currentTeam?.id) ); }; diff --git a/frontend/pages/queries/details/QueryDetailsPage/QueryDetailsPage.tsx b/frontend/pages/queries/details/QueryDetailsPage/QueryDetailsPage.tsx index 440e59ca49..d8fdb10d7a 100644 --- a/frontend/pages/queries/details/QueryDetailsPage/QueryDetailsPage.tsx +++ b/frontend/pages/queries/details/QueryDetailsPage/QueryDetailsPage.tsx @@ -22,6 +22,8 @@ import { isTeamObserver, } from "utilities/permissions/permissions"; import { DOCUMENT_TITLE_SUFFIX } from "utilities/constants"; +import { buildQueryStringFromParams } from "utilities/url"; +import useTeamIdParam from "hooks/useTeamIdParam"; import Spinner from "components/Spinner/Spinner"; import Button from "components/buttons/Button"; @@ -65,9 +67,13 @@ const QueryDetailsPage = ({ router.push(PATHS.MANAGE_QUERIES); } const queryParams = location.query; - const teamId = location.query.team_id - ? parseInt(location.query.team_id, 10) - : undefined; + + const { currentTeamId } = useTeamIdParam({ + location, + router, + includeAllTeams: true, + includeNoTeam: false, + }); // Functions to avoid race conditions const serverSortBy: ISortOption[] = (() => { @@ -203,7 +209,12 @@ const QueryDetailsPage = ({ // Function instead of constant eliminates race condition with filteredQueriesPath const backToQueriesPath = () => { - return filteredQueriesPath || PATHS.MANAGE_QUERIES; + return ( + filteredQueriesPath || + `${PATHS.MANAGE_QUERIES}?${buildQueryStringFromParams({ + team_id: currentTeamId, + })}` + ); }; return ( @@ -233,7 +244,8 @@ const QueryDetailsPage = ({ {canEditQuery && (

@@ -274,8 +274,8 @@ module.exports = { @@ -296,8 +296,8 @@ module.exports = {

Open-source device management

Lighter than air

diff --git a/website/views/layouts/layout.ejs b/website/views/layouts/layout.ejs index 0d069e0dac..dd6720d902 100644 --- a/website/views/layouts/layout.ejs +++ b/website/views/layouts/layout.ejs @@ -8,6 +8,7 @@ var hideHeaderLinks;// Hides the header navigation links. var hideFooterLinks;// Hides footer links, reduces the height of the footer to 60px; var showAdminLinks;// Shows links to admin pages to admin users. + var hideGetStartedButton;// Hides the 'Get started' button in the website's navigation header. // Applies personalization for who people come from ads, so that the website makes more sense for them: var primaryBuyingSituation; @@ -217,7 +218,9 @@ HTML Email preview tool <%}%> - Talk to us + <%if(!hideGetStartedButton){%> + Start now + <% }%> <%/* Desktop Navigation bar */%> @@ -264,7 +267,9 @@ - Talk to us + <%if(!hideGetStartedButton){%> + Start now + <% }%> <% if(_.has(me, 'id')) {%> Log out <% }%> diff --git a/website/views/pages/device-management.ejs b/website/views/pages/device-management.ejs index aa0a3eb1de..ce16776c7c 100644 --- a/website/views/pages/device-management.ejs +++ b/website/views/pages/device-management.ejs @@ -18,8 +18,8 @@ β€œZero” trust, fewer tickets

You can use Fleet’s API to customize every aspect of conditional access – even the stuff your CISO hasn’t thought of yet.

@@ -206,8 +206,8 @@

Device management (MDM)

Manage everything in one place

diff --git a/website/views/pages/endpoint-ops.ejs b/website/views/pages/endpoint-ops.ejs index f041ac90c4..90eadceb08 100644 --- a/website/views/pages/endpoint-ops.ejs +++ b/website/views/pages/endpoint-ops.ejs @@ -18,8 +18,8 @@ Osquery on easy mode

You don’t need to be an osquery expert to get the answers you need from your devices, Fleet does some of that for you.

@@ -194,8 +194,8 @@

Endpoint operations

A consistent interface

diff --git a/website/views/pages/entrance/signup.ejs b/website/views/pages/entrance/signup.ejs index eb7ef5bec5..c5c7b6069b 100644 --- a/website/views/pages/entrance/signup.ejs +++ b/website/views/pages/entrance/signup.ejs @@ -43,19 +43,6 @@ -
- -
- -
-
Please select an option.
-

This email is already linked to a Fleet account.
Please sign in with your email and password.

diff --git a/website/views/pages/homepage.ejs b/website/views/pages/homepage.ejs index 5087db21ee..9a8c3655fc 100644 --- a/website/views/pages/homepage.ejs +++ b/website/views/pages/homepage.ejs @@ -10,8 +10,8 @@

<%- partial('../partials/primary-tagline.partial.ejs') %>

Replace the sprawl with open-source code that works the way you want.

@@ -277,8 +277,8 @@

For teams with lots of different endpoints

<%- partial('../partials/primary-tagline.partial.ejs') %>

diff --git a/website/views/pages/imagine/higher-education.ejs b/website/views/pages/imagine/higher-education.ejs index 67272edc1e..5310abb0c6 100644 --- a/website/views/pages/imagine/higher-education.ejs +++ b/website/views/pages/imagine/higher-education.ejs @@ -8,8 +8,8 @@

Higher education meets simplified security

Automate security workflows in a single application by creating or installing policies to identify which devices comply with your security guidelines.

@@ -85,8 +85,8 @@

Open-source device management

Think for yourself

diff --git a/website/views/pages/imagine/jamf-alternative.ejs b/website/views/pages/imagine/jamf-alternative.ejs index 599003ccb9..8f002393b7 100644 --- a/website/views/pages/imagine/jamf-alternative.ejs +++ b/website/views/pages/imagine/jamf-alternative.ejs @@ -8,8 +8,8 @@

Fleet brings GitOps to MDM

Bringh-your-own MDM. Enjoy enterprise-ready open-source MDM and leverage the best of DevOps and GitOps inside a full-featured Macbook MDM.

@@ -59,8 +59,8 @@

Open-source device management

Lighter than air

diff --git a/website/views/pages/imagine/rapid-7-alternative.ejs b/website/views/pages/imagine/rapid-7-alternative.ejs index 0f4b5e19e5..3cd081e0db 100644 --- a/website/views/pages/imagine/rapid-7-alternative.ejs +++ b/website/views/pages/imagine/rapid-7-alternative.ejs @@ -8,8 +8,8 @@

Simplify vulnerability management

Fleet, an open-source security platform, empowers you to take control of vulnerability management for your organization. Designed with modern DevOps practices in mind, Fleet offers superior visibility, rapid response capabilities, and seamless integration with your existing workflows.

@@ -100,8 +100,8 @@

Open-source device management

Lighter than air

diff --git a/website/views/pages/imagine/unused-software.ejs b/website/views/pages/imagine/unused-software.ejs index c31434487e..5f62c7684b 100644 --- a/website/views/pages/imagine/unused-software.ejs +++ b/website/views/pages/imagine/unused-software.ejs @@ -8,8 +8,8 @@

Discover unused software licenses and optimize your IT budget

Fleet, the leading open-source, flexible device management solution, offers unprecedented visibility into your IT infrastructure, making it the ideal tool to discover and manage unused software licenses. This capability is essential to unlocking more IT budget, enhancing security, and ultimately improving the employee experience.

@@ -64,8 +64,8 @@ diff --git a/website/views/pages/pricing.ejs b/website/views/pages/pricing.ejs index 40dc82125c..e69f64189a 100644 --- a/website/views/pages/pricing.ejs +++ b/website/views/pages/pricing.ejs @@ -43,7 +43,7 @@ diff --git a/website/views/pages/start.ejs b/website/views/pages/start.ejs index e48d94933c..ea6dad2f8d 100644 --- a/website/views/pages/start.ejs +++ b/website/views/pages/start.ejs @@ -1,23 +1,519 @@
-
-

Welcome to Fleet

-

Spin up a local demo or get your Fleet Premium license key.

+
+ <%// β”Œβ”€β”β”Œβ”¬β”β”Œβ”€β”β”¬β”€β”β”Œβ”¬β” + // └─┐ β”‚ β”œβ”€β”€β”œβ”¬β”˜ β”‚ + // β””β”€β”˜ β”΄ β”΄ ┴┴└─ β”΄ %> +
+
+

Let’s get started

+

To see whether Fleet’s right for your team, let's have a look at your hosts and what you're trying to do.

+

You can come back at any time and pick up where you left off.

+ +
+ Start +
+
+
-
- - Run a local demo of Fleet -

Try Fleet

-

Run a local demo of Fleet

-
- - Purchase a Fleet Premium license -

Start

-

Purchase a Fleet Premium license

-
+ <%// ┬ ┬┬ β”¬β”Œβ”€β”β”Œβ”¬β” β”Œβ”€β”β”¬β”€β”β”Œβ”€β” ┬ β”¬β”Œβ”€β”β”¬ ┬ ┬ β”¬β”Œβ”€β”β”¬β”Œβ”β”Œβ”Œβ”€β” β”Œβ”€β”β”¬ β”Œβ”€β”β”Œβ”€β”β”Œβ”¬β” β”Œβ”€β”β”Œβ”€β”β”¬β”€β” + // β”‚β”‚β”‚β”œβ”€β”€β”œβ”€β”€ β”‚ β”œβ”€β”€β”œβ”¬β”˜β”œβ”€ β””β”¬β”˜β”‚ β”‚β”‚ β”‚ β”‚ │└─┐│││││ ┬ β”œβ”€ β”‚ β”œβ”€ β”œβ”€ β”‚ β”œβ”€ β”‚ β”‚β”œβ”¬β”˜ + // β””β”΄β”˜β”΄ β”΄β”΄ β”΄ β”΄ β”΄ β”΄β”΄β””β”€β””β”€β”˜ β”΄ β””β”€β”˜β””β”€β”˜ β””β”€β”˜β””β”€β”˜β”΄β”˜β””β”˜β””β”€β”˜ β”” β”΄β”€β”˜β””β”€β”˜β””β”€β”˜ β”΄ β”” β””β”€β”˜β”΄β””β”€%> +
+
+
+ πŸ† +
+

What will you use Fleet for?

+ +
+ + + + +
Please select an option
+
+ +
+ Continue +
+
+
+ <%// ┬ β”¬β”Œβ”€β”β”¬ β”¬β”Œβ”€β” ┬ β”¬β”Œβ”€β”β”¬ ┬ β”Œβ”€β”β”¬ β”¬β”Œβ”€β”β”¬β”€β” ┬ β”¬β”Œβ”€β”β”Œβ”€β”β”Œβ”¬β” β”Œβ”€β”β”¬ β”Œβ”€β”β”Œβ”€β”β”Œβ”¬β” + // β”œβ”€β”€β”œβ”€β”€β””β”β”Œβ”˜β”œβ”€ β””β”¬β”˜β”‚ β”‚β”‚ β”‚ β”œβ”€ β””β”β”Œβ”˜β”œβ”€ β”œβ”¬β”˜ β”‚ β”‚β””β”€β”β”œβ”€ β”‚β”‚ β”œβ”€ β”‚ β”œβ”€ β”œβ”€ β”‚ + // β”΄ β”΄β”΄ β”΄ β””β”˜ β””β”€β”˜ β”΄ β””β”€β”˜β””β”€β”˜ β””β”€β”˜ β””β”˜ β””β”€β”˜β”΄β””β”€ β””β”€β”˜β””β”€β”˜β””β”€β”˜β”€β”΄β”˜ β”” β”΄β”€β”˜β””β”€β”˜β””β”€β”˜ β”΄ %> +
+
+
+ πŸ† +
+

Have you ever used Fleet?

+ + +
+ + + + + +
Please select an option
+
+ +
+ Back + Continue +
+
+
+ <%// ┬ β”¬β”Œβ”€β”β”¬ ┬ β”Œβ”¬β”β”Œβ”€β”β”Œβ”β”Œβ”¬ ┬ ┬ β”¬β”Œβ”€β”β”Œβ”€β”β”Œβ”¬β”β”Œβ”€β” + // β”œβ”€β”€β”‚ β”‚β”‚β”‚β”‚ β”‚β”‚β”‚β”œβ”€β”€β”‚β”‚β”‚β””β”¬β”˜ β”œβ”€β”€β”‚ │└─┐ β”‚ └─┐ + // β”΄ β”΄β””β”€β”˜β””β”΄β”˜ β”΄ β”΄β”΄ β”΄β”˜β””β”˜ β”΄ β”΄ β”΄β””β”€β”˜β””β”€β”˜ β”΄ β””β”€β”˜%> +
+
+
+ πŸ† +
+

How many hosts do you have:

+ +
+ +
+ + + + +
Please select an option
+
+
+ +
+ Back + Continue +
+
+
+ <%// ┬ ┬┬┬ ┬ ┬ β”¬β”Œβ”€β”β”¬ ┬ β”Œβ” β”Œβ”€β” β”Œβ”€β”β”Œβ”€β”β”¬ β”Œβ”€β” ┬ β”¬β”Œβ”€β”β”Œβ”€β”β”Œβ”¬β”β”¬β”Œβ”β”Œβ”Œβ”€β” + // β”‚β”‚β”‚β”‚β”‚ β”‚ β””β”¬β”˜β”‚ β”‚β”‚ β”‚ β”œβ”΄β”β”œβ”€ β””β”€β”β”œβ”€ β”‚ β”œβ”€ β”œβ”€β”€β”‚ │└─┐ β”‚ β”‚β”‚β”‚β”‚β”‚ ┬ + // β””β”΄β”˜β”΄β”΄β”€β”˜β”΄β”€β”˜ β”΄ β””β”€β”˜β””β”€β”˜ β””β”€β”˜β””β”€β”˜ β””β”€β”˜β””β”€β”˜β”΄β”€β”˜β”” β”΄ β”΄β””β”€β”˜β””β”€β”˜ β”΄ β”΄β”˜β””β”˜β””β”€β”˜%> +
+
+
+ πŸ† +
+

Will you be hosting Fleet yourself?

+ + +
+ + +
Please select an option
+
+ +
+ Back + Continue +
+
+
+ <%// ┬ ┬┬ β”¬β”Œβ”€β”β”Œβ”¬β” β”Œβ”€β”β”¬β”€β”β”Œβ”€β” ┬ β”¬β”Œβ”€β”β”¬ ┬ ┬ β”¬β”Œβ”€β”β”¬β”€β”β”¬β”Œβ”€β”¬β”Œβ”β”Œβ”Œβ”€β” β”Œβ”€β”β”Œβ”β”Œ + // β”‚β”‚β”‚β”œβ”€β”€β”œβ”€β”€ β”‚ β”œβ”€β”€β”œβ”¬β”˜β”œβ”€ β””β”¬β”˜β”‚ β”‚β”‚ β”‚ β”‚β”‚β”‚β”‚ β”‚β”œβ”¬β”˜β”œβ”΄β”β”‚β”‚β”‚β”‚β”‚ ┬ β”‚ β”‚β”‚β”‚β”‚ ─── + // β””β”΄β”˜β”΄ β”΄β”΄ β”΄ β”΄ β”΄ β”΄β”΄β””β”€β””β”€β”˜ β”΄ β””β”€β”˜β””β”€β”˜ β””β”΄β”˜β””β”€β”˜β”΄β””β”€β”΄ β”΄β”΄β”˜β””β”˜β””β”€β”˜ β””β”€β”˜β”˜β””β”˜ + // β”Œβ”€β”β”Œβ”€β” β”Œβ”€β”β”Œβ”€β”β”Œβ”€β”β”¬ β”¬β”¬β”€β”β”¬β”Œβ”¬β”β”¬ ┬ + // β”œβ”€ β”‚ β”‚β”€β”€β”€β””β”€β”β”œβ”€ β”‚ β”‚ β”‚β”œβ”¬β”˜β”‚ β”‚ β””β”¬β”˜ + // β””β”€β”˜β””β”€β”˜ β””β”€β”˜β””β”€β”˜β””β”€β”˜β””β”€β”˜β”΄β””β”€β”΄ β”΄ β”΄%> +
+
+
+ πŸ† +
+

What are you working on, mainly?

+ +
+ +
+ + + + + + +
Please select an option
+
+
+ +
+ Back + Continue +
+
+
+ <%// β”¬β”Œβ”€β” β”¬β”Œβ”¬β” β”Œβ”€β”β”Œβ”β”Œβ”¬ ┬ β”Œβ”€β”β”Œβ”€β”β”Œβ”€β”β”Œβ”¬β” + // │└─┐ β”‚ β”‚ β”œβ”€β”€β”‚β”‚β”‚β””β”¬β”˜ β”‚ ┬│ β”‚β”‚ β”‚ β”‚β”‚ + // β”΄β””β”€β”˜ β”΄ β”΄ β”΄ β”΄β”˜β””β”˜ β”΄ β””β”€β”˜β””β”€β”˜β””β”€β”˜β”€β”΄β”˜%> +
+ +
+
+ πŸ† +
+
+

Is it any good?

+

We don’t have a public sample environment, but you can try Fleet out locally on your computer.

+
+ +

You can come back here any time to continue with your deployment.

+
+ Back + Continue +
+
+
+ <%// ┬ ┬┬ β”¬β”Œβ”€β”β”Œβ”¬β” β”Œβ”¬β”β”¬β”Œβ”¬β” ┬ β”¬β”Œβ”€β”β”¬ ┬ β”Œβ”¬β”β”¬ β”¬β”¬β”Œβ”β”Œβ”¬β”Œβ”€ + // β”‚β”‚β”‚β”œβ”€β”€β”œβ”€β”€ β”‚ β”‚β”‚β”‚ β”‚β”‚ β””β”¬β”˜β”‚ β”‚β”‚ β”‚ β”‚ β”œβ”€β”€β”‚β”‚β”‚β”‚β”œβ”΄β” + // β””β”΄β”˜β”΄ β”΄β”΄ β”΄ β”΄ β”€β”΄β”˜β”΄β”€β”΄β”˜ β”΄ β””β”€β”˜β””β”€β”˜ β”΄ β”΄ β”΄β”΄β”˜β””β”˜β”΄ β”΄%> +
+
+
+ πŸ† +
+

What did you think?

+

Now that you’ve seen what Fleet can do, what do you want to do next?

+ +
+
+ + + I’d like you to host Fleet for me +
+ + +
Please select an option
+
+ +
+
Deloitte logo
+

+ Something I really appreciate about working with you guys is that it doesn't feel like I'm talking to a vendor. It actually feels like I'm talking to my team, and I really appreciate it. +

+
+
+ Chandra Majumdar +
+
+

Chandra Majumdar

+

Partner - Cyber and Strategic Risk

+
+
+
+
+
Uber logo
+

+ Exciting. This is a team that listens to feedback. +

+
+
+ Erik Gomez +
+
+

Erik Gomez

+

Staff Client Platform Engineer

+
+
+
+
+

+ The visibility down into the assets covered by the agent is phenomenal. Fleet has become the central source for a lot of things. +

+
+
+ Andre Shields +
+
+

Andre Shields

+

Staff Cybersecurity Engineer, Vulnerability Management

+
+
+
+
+
Deputy logo
+

+ When we look at vendors, we look for ones that are very receptive to feedback, where you’re just part of the family, I guess. Fleet’s really good at that. +

+
+
+ Harrison Ravazzolo +
+
+

Harrison Ravazzolo

+

Lead platform and identity engineer

+
+
+
+
+
Atlassian logo
+

+ I love the steady and consistent delivery of features that help teams work how they want to work, not how your product dictates they work. +

+
+
+ Dan Grzelak +
+
+

Dan Grzelak

+

Security Chief of Staff

+
+
+
+
+ Back + Continue +
+
+
+ <%// ┬ β”Œβ”€β”β”Œβ”¬β”β”Œβ”€β” β”Œβ”¬β”β”Œβ”€β”β”¬ β”¬β”Œβ”€ β”Œβ”¬β”β”Œβ”€β” ┬ β”¬β”Œβ”€β”β”¬ ┬┬─┐ β”Œβ”¬β”β”Œβ”€β”β”Œβ”€β”β”Œβ”¬β” + // β”‚ β”œβ”€ β”‚ └─┐ β”‚ β”œβ”€β”€β”‚ β”œβ”΄β” β”‚ β”‚ β”‚ β””β”¬β”˜β”‚ β”‚β”‚ β”‚β”œβ”¬β”˜ β”‚ β”œβ”€ β”œβ”€β”€β”‚β”‚β”‚ + // β”΄β”€β”˜β””β”€β”˜ β”΄ β””β”€β”˜ β”΄ β”΄ β”΄β”΄β”€β”˜β”΄ β”΄ β”΄ β””β”€β”˜ β”΄ β””β”€β”˜β””β”€β”˜β”΄β””β”€ β”΄ β””β”€β”˜β”΄ β”΄β”΄ β”΄%> +
+
+
+ πŸ† +
+
+

Let’s talk to your team

+

Great, let’s jump on a call to talk more about your fleet.

+
+ +
+ Back +
+
+ <%// β•”β•¦β•—β”Œβ”€β”β”Œβ”€β”β”¬ β”Œβ”€β”β”¬ ┬ β”Œβ”€β”β”¬ β”Œβ”€β”β”Œβ”€β”β”Œβ”¬β” β”¬β”Œβ”β”Œ ┬ β”¬β”Œβ”€β”β”¬ ┬┬─┐ β”Œβ”€β”β”Œβ”β”Œβ”¬ β”¬β”¬β”¬β”€β”β”Œβ”€β”β”Œβ”β”Œβ”Œβ”¬β”β”Œβ”€β”β”Œβ”β”Œβ”Œβ”¬β” + // β•‘β•‘β”œβ”€ β”œβ”€β”˜β”‚ β”‚ β”‚β””β”¬β”˜ β”œβ”€ β”‚ β”œβ”€ β”œβ”€ β”‚ β”‚β”‚β”‚β”‚ β””β”¬β”˜β”‚ β”‚β”‚ β”‚β”œβ”¬β”˜ β”œβ”€ β”‚β”‚β”‚β””β”β”Œβ”˜β”‚β”œβ”¬β”˜β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚β”œβ”€ β”‚β”‚β”‚ β”‚ + // β•β•©β•β””β”€β”˜β”΄ β”΄β”€β”˜β””β”€β”˜ β”΄ β”” β”΄β”€β”˜β””β”€β”˜β””β”€β”˜ β”΄ β”΄β”˜β””β”˜ β”΄ β””β”€β”˜β””β”€β”˜β”΄β””β”€ β””β”€β”˜β”˜β””β”˜ β””β”˜ β”΄β”΄β””β”€β””β”€β”˜β”˜β””β”˜β”΄ β”΄β””β”€β”˜β”˜β””β”˜ β”΄%> +
+
+
+ πŸ† +
+
+

Deploy Fleet in your environment

+

Learn how to deploy and roll out Fleet in your environment.

+
+ +
+ +
+
+ Back +
+
+ <%// β”Œβ”€β”β”Œβ”€β”β”¬ β”Œβ”€β” ┬ β”¬β”Œβ”€β”β”Œβ”€β”β”Œβ”¬β”β”Œβ”€β”β”Œβ”¬β” β”Œβ”¬β”β”Œβ”€β”β”Œβ”€β”β”¬ β”Œβ”€β”β”¬ ┬ + // β””β”€β”β”œβ”€ β”‚ β”œβ”€β”€β”€β”€β”œβ”€β”€β”‚ │└─┐ β”‚ β”œβ”€ β”‚β”‚ β”‚β”‚β”œβ”€ β”œβ”€β”˜β”‚ β”‚ β”‚β””β”¬β”˜ + // β””β”€β”˜β””β”€β”˜β”΄β”€β”˜β”” β”΄ β”΄β””β”€β”˜β””β”€β”˜ β”΄ β””β”€β”˜β”€β”΄β”˜ β”€β”΄β”˜β””β”€β”˜β”΄ β”΄β”€β”˜β””β”€β”˜ β”΄%> +
+
+
+ πŸ† +
+
+

Deploy Fleet in your environment

+

Learn how to deploy and rollout Fleet in your environment, get a Fleet Premium license, or both. You can come back in any time to upgrade.

+
+ +
+ Back +
+
+ <%// β”Œβ”¬β”β”Œβ”€β”β”Œβ”β”Œβ”Œβ”€β”β”Œβ”€β”β”Œβ”€β”β”Œβ”¬β” β”Œβ”€β”β”¬ β”Œβ”€β”β”¬ β”¬β”Œβ”¬β” β”Œβ”€β”β”Œβ”€β”β”¬β”€β” β”Œβ”€β”β”¬β”€β”β”Œβ”€β”β”¬ β”¬β”¬β”Œβ”β”Œβ”Œβ”€β” + // β”‚β”‚β”‚β”œβ”€β”€β”‚β”‚β”‚β”œβ”€β”€β”‚ β”¬β”œβ”€ β”‚β”‚ β”‚ β”‚ β”‚ β”‚β”‚ β”‚ β”‚β”‚ β”œβ”€ β”‚ β”‚β”œβ”¬β”˜ β”‚ β”¬β”œβ”¬β”˜β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚β”‚β”‚ ┬ + // β”΄ β”΄β”΄ β”΄β”˜β””β”˜β”΄ β”΄β””β”€β”˜β””β”€β”˜β”€β”΄β”˜ β””β”€β”˜β”΄β”€β”˜β””β”€β”˜β””β”€β”˜β”€β”΄β”˜ β”” β””β”€β”˜β”΄β””β”€ β””β”€β”˜β”΄β””β”€β””β”€β”˜β””β”΄β”˜β”΄β”˜β””β”˜β””β”€β”˜ + // β”Œβ”¬β”β”Œβ”€β”β”Œβ”€β”β”¬ β”Œβ”€β”β”¬ β”¬β”Œβ”¬β”β”Œβ”€β”β”Œβ”β”Œβ”Œβ”¬β”β”Œβ”€β” + // β”‚β”‚β”œβ”€ β”œβ”€β”˜β”‚ β”‚ β”‚β””β”¬β”˜β”‚β”‚β”‚β”œβ”€ β”‚β”‚β”‚ β”‚ └─┐ + // β”€β”΄β”˜β””β”€β”˜β”΄ β”΄β”€β”˜β””β”€β”˜ β”΄ β”΄ β”΄β””β”€β”˜β”˜β””β”˜ β”΄ β””β”€β”˜%> +
+
+
+ πŸ† +
+
+

Managed cloud for growing deployments

+

Unfortunately, managed cloud hosting is not yet available for growing deployments of less than 700 hosts.

+
+ +
+ Back +
+
+ <%// ┬ β”¬β”Œβ”€β”β”¬ β”Œβ”€β”β”Œβ”€β”β”Œβ”¬β”β”Œβ”€β” β”Œβ”¬β”β”Œβ”€β” β”Œβ”€β”β”¬ β”Œβ”€β”β”Œβ”€β”β”Œβ”¬β” + // β”‚β”‚β”‚β”œβ”€ β”‚ β”‚ β”‚ β”‚β”‚β”‚β”‚β”œβ”€ β”‚ β”‚ β”‚ β”œβ”€ β”‚ β”œβ”€ β”œβ”€ β”‚ + // β””β”΄β”˜β””β”€β”˜β”΄β”€β”˜β””β”€β”˜β””β”€β”˜β”΄ β”΄β””β”€β”˜ β”΄ β””β”€β”˜ β”” β”΄β”€β”˜β””β”€β”˜β””β”€β”˜ β”΄%> +
+
+

Welcome to Fleet

+

Spin up a local demo or get your Fleet Premium license key.

+
+ +
+ +
+
+ Back +
-
-
diff --git a/website/views/pages/vulnerability-management.ejs b/website/views/pages/vulnerability-management.ejs index 6971d60fe4..886e50a9e5 100644 --- a/website/views/pages/vulnerability-management.ejs +++ b/website/views/pages/vulnerability-management.ejs @@ -18,8 +18,8 @@ Untangle your security stack

Use open data and APIs to connect your vulnerability solution with osquery, the agent you might already have deployed.

@@ -142,8 +142,8 @@

Open-source vulnerability management

Build the vulnerability program you actually want

From 63c096b10206a835b92acb18219f3da63b695684 Mon Sep 17 00:00:00 2001 From: Victor Lyuboslavsky Date: Thu, 4 Apr 2024 11:24:54 -0500 Subject: [PATCH 24/60] Waiving CVE-2023-32698 (#18056) #17359 Waiving CVE-2023-32698 --- .trivyignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.trivyignore b/.trivyignore index a2f263d8f9..229b985973 100644 --- a/.trivyignore +++ b/.trivyignore @@ -12,3 +12,8 @@ CVE-2020-7753 # We feel like the risk of DoS using this technique, which requires being logged in, is low probability and low impact, as such we will not update glob-parent only for this CVE CVE-2020-28469 + +# 2024/04/04 (github.com/goreleaser/nfpm/v2 should be updated) +# When packaging linux files, we do not use global permissions. Manually verified that packed fleet-osquery files do not have group/global write permissions. + +CVE-2023-32698 From 74761b75d818648cdfc104bb8c0fc4019fd83035 Mon Sep 17 00:00:00 2001 From: Jacob Shandling <61553566+jacobshandling@users.noreply.github.com> Date: Thu, 4 Apr 2024 09:38:10 -0700 Subject: [PATCH 25/60] =?UTF-8?q?Update=20carve=20request=20block=20id=20m?= =?UTF-8?q?ismatch=20error=20code=20from=20500=20=E2=80=93>=20400=20(#1797?= =?UTF-8?q?4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Addresses #16951 # Checklist for submitter If some of the following don't apply, delete the relevant line. - [x] Changes file added for user-visible changes in `changes/` - [x] Added/updated tests - [ ] Manual QA for all new/changed functionality – @ksatter can you please add reproduction steps to the issue to aid in manual QA? --------- Co-authored-by: Jacob Shandling --- changes/16951-improve-carve-request-timeout-error-code | 1 + server/service/carves.go | 2 +- server/service/carves_test.go | 5 +++-- server/service/integration_core_test.go | 9 ++++----- 4 files changed, 9 insertions(+), 8 deletions(-) create mode 100644 changes/16951-improve-carve-request-timeout-error-code diff --git a/changes/16951-improve-carve-request-timeout-error-code b/changes/16951-improve-carve-request-timeout-error-code new file mode 100644 index 0000000000..c23c1bb466 --- /dev/null +++ b/changes/16951-improve-carve-request-timeout-error-code @@ -0,0 +1 @@ +* Made block_id mismatch errors more informative as 400s instead of 500s. diff --git a/server/service/carves.go b/server/service/carves.go index 36205a73ee..216a18065a 100644 --- a/server/service/carves.go +++ b/server/service/carves.go @@ -297,7 +297,7 @@ func (svc *Service) CarveBlock(ctx context.Context, payload fleet.CarveBlockPayl logging.WithExtras(ctx, "validate_carve_error", errRecord, "carve_id", carve.ID) } - return ctxerr.Wrap(ctx, err, "validate carve block") + return ctxerr.Wrap(ctx, badRequest("validate carve block"), err.Error()) } if err := svc.carveStore.NewBlock(ctx, carve, payload.BlockId, payload.Data); err != nil { diff --git a/server/service/carves_test.go b/server/service/carves_test.go index d338fd4399..e02c71cb8f 100644 --- a/server/service/carves_test.go +++ b/server/service/carves_test.go @@ -477,7 +477,7 @@ func TestCarveCarveBlockBlockCountExceedError(t *testing.T) { assert.Contains(t, err.Error(), "block_id exceeds expected max") } -func TestCarveCarveBlockBlockCountMatchError(t *testing.T) { +func TestCarveBlockCountMatchError(t *testing.T) { sessionId := "foobar" metadata := &fleet.CarveMetadata{ ID: 2, @@ -509,7 +509,8 @@ func TestCarveCarveBlockBlockCountMatchError(t *testing.T) { } err := svc.CarveBlock(context.Background(), payload) - require.Error(t, err) + var be *fleet.BadRequestError + require.ErrorAs(t, err, &be) assert.Contains(t, err.Error(), "block_id does not match") } diff --git a/server/service/integration_core_test.go b/server/service/integration_core_test.go index ac358259b1..87640f3ed1 100644 --- a/server/service/integration_core_test.go +++ b/server/service/integration_core_test.go @@ -5367,7 +5367,6 @@ func (s *integrationTestSuite) TestGoogleCalendarIntegrations() { }`, domain, )), http.StatusUnprocessableEntity, ) - } func (s *integrationTestSuite) TestQueriesBadRequests() { @@ -6866,7 +6865,7 @@ func (s *integrationTestSuite) TestCarve() { SessionId: sid, RequestId: "r1", Data: []byte("p1."), - }, http.StatusInternalServerError, &blockResp) // TODO: should be 400, see #4406 + }, http.StatusBadRequest, &blockResp) checkCarveError(1, "block_id does not match expected block (0): 1") // sending a block with valid payload, block 0 @@ -6895,7 +6894,7 @@ func (s *integrationTestSuite) TestCarve() { SessionId: sid, RequestId: "r1", Data: []byte("p2."), - }, http.StatusInternalServerError, &blockResp) // TODO: should be 400, see #4406 + }, http.StatusBadRequest, &blockResp) checkCarveError(1, "block_id does not match expected block (2): 1") // sending final block with too many bytes @@ -6905,7 +6904,7 @@ func (s *integrationTestSuite) TestCarve() { SessionId: sid, RequestId: "r1", Data: []byte("p3extra"), - }, http.StatusInternalServerError, &blockResp) // TODO: should be 400, see #4406 + }, http.StatusBadRequest, &blockResp) checkCarveError(1, "exceeded declared block size 3: 7") // sending actual final block @@ -6925,7 +6924,7 @@ func (s *integrationTestSuite) TestCarve() { SessionId: sid, RequestId: "r1", Data: []byte("p4."), - }, http.StatusInternalServerError, &blockResp) // TODO: should be 400, see #4406 + }, http.StatusBadRequest, &blockResp) checkCarveError(1, "block_id exceeds expected max (2): 3") } From 77c8adf4bbf035d92f7d55a7994c3d88726fabb0 Mon Sep 17 00:00:00 2001 From: Jacob Shandling <61553566+jacobshandling@users.noreply.github.com> Date: Thu, 4 Apr 2024 10:16:19 -0700 Subject: [PATCH 26/60] Add frontend dev pattern for handlers (#18061) Pattern doc updates per today's frontend sync Co-authored-by: Jacob Shandling --- frontend/docs/patterns.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/frontend/docs/patterns.md b/frontend/docs/patterns.md index 0ba21794d7..20406cce17 100644 --- a/frontend/docs/patterns.md +++ b/frontend/docs/patterns.md @@ -138,6 +138,24 @@ We tend to use explicit assignment of prop values, instead of object spread synt ``` ``` + +### Naming handlers +When defining component props for handlers, we prefer naming with a more general `onAction`. When +naming the handler passed into that prop or used in the same component it's defined, we prefer +either the same `onAction` or, if useful, a more specific `onMoreSpecifiedAction`. E.g.: + +```tsx + +``` +or +```tsx + +``` + ### Page component pattern When creating a **top level page** (e.g. dashboard page, hosts page, policies page) From e8ca9598882deaaceefa6740e109425d35e96aa1 Mon Sep 17 00:00:00 2001 From: Lucas Manuel Rodriguez Date: Thu, 4 Apr 2024 14:58:31 -0300 Subject: [PATCH 27/60] Add enterprise integration test for calendar events (#17900) Integration tests for the calendar feature: #17441. Adding coverage screenshots for the calendar cron and the osquery distributed/write coverage: ![Screenshot 2024-03-27 at 14 20 44](https://github.com/fleetdm/fleet/assets/2073526/40d394ab-2208-4bec-981b-fe22fae8b5c1) ![Screenshot 2024-03-27 at 14 21 20](https://github.com/fleetdm/fleet/assets/2073526/1e4c8611-21ba-48a6-82f8-a163594f7f01) --- cmd/fleet/serve.go | 5 +- ee/server/calendar/google_calendar_mock.go | 11 + {cmd/fleet => server/cron}/calendar_cron.go | 15 +- .../cron}/calendar_cron_test.go | 104 +-- server/datastore/mysql/calendar_events.go | 2 +- server/datastore/mysql/policies.go | 1 - server/fleet/datastore.go | 5 + server/service/integration_enterprise_test.go | 676 +++++++++++++++++- server/service/schedule/schedule.go | 10 +- server/service/testing_client.go | 6 + 10 files changed, 763 insertions(+), 72 deletions(-) rename {cmd/fleet => server/cron}/calendar_cron.go (98%) rename {cmd/fleet => server/cron}/calendar_cron_test.go (88%) diff --git a/cmd/fleet/serve.go b/cmd/fleet/serve.go index d0ca94a81c..1fdd588a9e 100644 --- a/cmd/fleet/serve.go +++ b/cmd/fleet/serve.go @@ -28,6 +28,7 @@ import ( configpkg "github.com/fleetdm/fleet/v4/server/config" "github.com/fleetdm/fleet/v4/server/contexts/ctxerr" licensectx "github.com/fleetdm/fleet/v4/server/contexts/license" + "github.com/fleetdm/fleet/v4/server/cron" "github.com/fleetdm/fleet/v4/server/datastore/cached_mysql" "github.com/fleetdm/fleet/v4/server/datastore/mysql" "github.com/fleetdm/fleet/v4/server/datastore/mysqlredis" @@ -773,9 +774,7 @@ the way that the Fleet server works. if license.IsPremium() { if err := cronSchedules.StartCronSchedule( func() (fleet.CronSchedule, error) { - return newCalendarSchedule( - ctx, instanceID, ds, logger, - ) + return cron.NewCalendarSchedule(ctx, instanceID, ds, 5*time.Minute, logger) }, ); err != nil { initFatal(err, "failed to register calendar schedule") diff --git a/ee/server/calendar/google_calendar_mock.go b/ee/server/calendar/google_calendar_mock.go index 08e3a72e20..1dd6f16bb4 100644 --- a/ee/server/calendar/google_calendar_mock.go +++ b/ee/server/calendar/google_calendar_mock.go @@ -93,3 +93,14 @@ func ClearMockEvents() { defer mu.Unlock() mockEvents = make(map[string]*calendar.Event) } + +func SetMockEventsToNow() { + mu.Lock() + defer mu.Unlock() + + now := time.Now() + for _, mockEvent := range mockEvents { + mockEvent.Start = &calendar.EventDateTime{DateTime: now.Format(time.RFC3339)} + mockEvent.End = &calendar.EventDateTime{DateTime: now.Add(30 * time.Minute).Format(time.RFC3339)} + } +} diff --git a/cmd/fleet/calendar_cron.go b/server/cron/calendar_cron.go similarity index 98% rename from cmd/fleet/calendar_cron.go rename to server/cron/calendar_cron.go index 54bd89f551..cad8ba8513 100644 --- a/cmd/fleet/calendar_cron.go +++ b/server/cron/calendar_cron.go @@ -1,4 +1,4 @@ -package main +package cron import ( "context" @@ -19,19 +19,19 @@ import ( const calendarConsumers = 18 -func newCalendarSchedule( +func NewCalendarSchedule( ctx context.Context, instanceID string, ds fleet.Datastore, + interval time.Duration, logger kitlog.Logger, ) (*schedule.Schedule, error) { const ( - name = string(fleet.CronCalendar) - defaultInterval = 5 * time.Minute + name = string(fleet.CronCalendar) ) logger = kitlog.With(logger, "cron", name) s := schedule.New( - ctx, name, instanceID, defaultInterval, ds, ds, + ctx, name, instanceID, interval, ds, ds, schedule.WithAltLockID("calendar"), schedule.WithLogger(logger), schedule.WithJob( @@ -318,9 +318,6 @@ func processFailingHostExistingCalendarEvent( } // Even if fields haven't changed we want to update the calendar_events.updated_at below. updated = true - // - // TODO(lucas): Check changing updatedEvent to UTC before consuming. - // } if updated { @@ -367,8 +364,6 @@ func processFailingHostExistingCalendarEvent( return fmt.Errorf("update host calendar webhook status: %w", err) } - // TODO(lucas): If this doesn't work at scale, then implement a special refetch - // for policies only. if err := ds.UpdateHostRefetchRequested(ctx, host.HostID, true); err != nil { return fmt.Errorf("refetch host: %w", err) } diff --git a/cmd/fleet/calendar_cron_test.go b/server/cron/calendar_cron_test.go similarity index 88% rename from cmd/fleet/calendar_cron_test.go rename to server/cron/calendar_cron_test.go index 4d9133377c..21e2a8fe1d 100644 --- a/cmd/fleet/calendar_cron_test.go +++ b/server/cron/calendar_cron_test.go @@ -1,11 +1,8 @@ -package main +package cron import ( "context" "fmt" - "io" - "net/http" - "net/http/httptest" "os" "strconv" "strings" @@ -17,7 +14,6 @@ import ( "github.com/fleetdm/fleet/v4/server/fleet" "github.com/fleetdm/fleet/v4/server/mock" kitlog "github.com/go-kit/log" - "github.com/stretchr/testify/require" ) @@ -207,16 +203,19 @@ func TestCalendarEventsMultipleHosts(t *testing.T) { calendar.ClearMockEvents() }) - // TODO(lucas): Test! - webhookServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - require.Equal(t, "POST", r.Method) - requestBodyBytes, err := io.ReadAll(r.Body) - require.NoError(t, err) - t.Logf("webhook request: %s\n", requestBodyBytes) - })) - t.Cleanup(func() { - webhookServer.Close() - }) + // + // Test setup + // + // team1: + // + // policyID1 (calendar) + // policyID2 (calendar) + // + // hostID1 has user1@example.com not passing policies. + // hostID2 has user2@example.com passing policies. + // hostID3 does not have example.com email and is not passing policies. + // hostID4 does not have example.com email and is passing policies. + // ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) { return &fleet.AppConfig{ @@ -242,7 +241,7 @@ func TestCalendarEventsMultipleHosts(t *testing.T) { Integrations: fleet.TeamIntegrations{ GoogleCalendar: &fleet.TeamGoogleCalendarIntegration{ Enable: true, - WebhookURL: webhookServer.URL, + WebhookURL: "https://foo.example.com", }, }, }, @@ -268,12 +267,13 @@ func TestCalendarEventsMultipleHosts(t *testing.T) { hostID1, userEmail1 := uint(100), "user1@example.com" hostID2, userEmail2 := uint(101), "user2@example.com" - hostID3, userEmail3 := uint(102), "user3@other.com" - hostID4, userEmail4 := uint(103), "user4@other.com" + hostID3 := uint(102) + hostID4 := uint(103) ds.GetTeamHostsPolicyMembershipsFunc = func( ctx context.Context, domain string, teamID uint, policyIDs []uint, ) ([]fleet.HostPolicyMembershipData, error) { + require.Equal(t, "example.com", domain) require.Equal(t, teamID1, teamID) require.Equal(t, []uint{policyID1, policyID2}, policyIDs) return []fleet.HostPolicyMembershipData{ @@ -289,12 +289,12 @@ func TestCalendarEventsMultipleHosts(t *testing.T) { }, { HostID: hostID3, - Email: userEmail3, + Email: "", // because it does not belong to example.com Passing: false, }, { HostID: hostID4, - Email: userEmail4, + Email: "", // because it does not belong to example.com Passing: true, }, }, nil @@ -304,6 +304,10 @@ func TestCalendarEventsMultipleHosts(t *testing.T) { return nil, nil, notFoundErr{} } + var eventsMu sync.Mutex + calendarEvents := make(map[string]*fleet.CalendarEvent) + hostCalendarEvents := make(map[uint]*fleet.HostCalendarEvent) + ds.CreateOrUpdateCalendarEventFunc = func(ctx context.Context, email string, startTime, endTime time.Time, @@ -311,26 +315,43 @@ func TestCalendarEventsMultipleHosts(t *testing.T) { hostID uint, webhookStatus fleet.CalendarWebhookStatus, ) (*fleet.CalendarEvent, error) { - switch email { - case userEmail1: - require.Equal(t, hostID1, hostID) - case userEmail2: - require.Equal(t, hostID2, hostID) - case userEmail3: - require.Equal(t, hostID3, hostID) - case userEmail4: - require.Equal(t, hostID4, hostID) - } + require.Equal(t, hostID1, hostID) + require.Equal(t, userEmail1, email) require.Equal(t, fleet.CalendarWebhookStatusNone, webhookStatus) require.NotEmpty(t, data) require.NotZero(t, startTime) require.NotZero(t, endTime) - // Currently, the returned calendar event is unused. + + eventsMu.Lock() + calendarEventID := uint(len(calendarEvents) + 1) + calendarEvents[email] = &fleet.CalendarEvent{ + ID: calendarEventID, + Email: email, + StartTime: startTime, + EndTime: endTime, + Data: data, + } + hostCalendarEventID := uint(len(hostCalendarEvents) + 1) + hostCalendarEvents[hostID] = &fleet.HostCalendarEvent{ + ID: hostCalendarEventID, + HostID: hostID, + CalendarEventID: calendarEventID, + WebhookStatus: webhookStatus, + } + eventsMu.Unlock() return nil, nil } err := cronCalendarEvents(ctx, ds, logger) require.NoError(t, err) + + eventsMu.Lock() + require.Len(t, calendarEvents, 1) + require.Len(t, hostCalendarEvents, 1) + eventsMu.Unlock() + + createdCalendarEvents := calendar.ListGoogleMockEvents() + require.Len(t, createdCalendarEvents, 1) } type notFoundErr struct{} @@ -356,17 +377,6 @@ func TestCalendarEvents1KHosts(t *testing.T) { calendar.ClearMockEvents() }) - // TODO(lucas): Use for the test. - webhookServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - require.Equal(t, "POST", r.Method) - requestBodyBytes, err := io.ReadAll(r.Body) - require.NoError(t, err) - t.Logf("webhook request: %s\n", requestBodyBytes) - })) - t.Cleanup(func() { - webhookServer.Close() - }) - ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) { return &fleet.AppConfig{ Integrations: fleet.Integrations{ @@ -395,7 +405,7 @@ func TestCalendarEvents1KHosts(t *testing.T) { Integrations: fleet.TeamIntegrations{ GoogleCalendar: &fleet.TeamGoogleCalendarIntegration{ Enable: true, - WebhookURL: webhookServer.URL, + WebhookURL: "https://foo.example.com", }, }, }, @@ -406,7 +416,7 @@ func TestCalendarEvents1KHosts(t *testing.T) { Integrations: fleet.TeamIntegrations{ GoogleCalendar: &fleet.TeamGoogleCalendarIntegration{ Enable: true, - WebhookURL: webhookServer.URL, + WebhookURL: "https://foo.example.com", }, }, }, @@ -417,7 +427,7 @@ func TestCalendarEvents1KHosts(t *testing.T) { Integrations: fleet.TeamIntegrations{ GoogleCalendar: &fleet.TeamGoogleCalendarIntegration{ Enable: true, - WebhookURL: webhookServer.URL, + WebhookURL: "https://foo.example.com", }, }, }, @@ -428,7 +438,7 @@ func TestCalendarEvents1KHosts(t *testing.T) { Integrations: fleet.TeamIntegrations{ GoogleCalendar: &fleet.TeamGoogleCalendarIntegration{ Enable: true, - WebhookURL: webhookServer.URL, + WebhookURL: "https://foo.example.com", }, }, }, @@ -439,7 +449,7 @@ func TestCalendarEvents1KHosts(t *testing.T) { Integrations: fleet.TeamIntegrations{ GoogleCalendar: &fleet.TeamGoogleCalendarIntegration{ Enable: true, - WebhookURL: webhookServer.URL, + WebhookURL: "https://foo.example.com", }, }, }, diff --git a/server/datastore/mysql/calendar_events.go b/server/datastore/mysql/calendar_events.go index ebd071d81a..91f508c169 100644 --- a/server/datastore/mysql/calendar_events.go +++ b/server/datastore/mysql/calendar_events.go @@ -52,7 +52,7 @@ func (ds *Datastore) CreateOrUpdateCalendarEvent( } else { stmt := `SELECT id FROM calendar_events WHERE email = ?` if err := sqlx.GetContext(ctx, tx, &id, stmt, email); err != nil { - return ctxerr.Wrap(ctx, err, "query mdm solution id") + return ctxerr.Wrap(ctx, err, "calendar event id") } } diff --git a/server/datastore/mysql/policies.go b/server/datastore/mysql/policies.go index 098014f699..0f593af8bc 100644 --- a/server/datastore/mysql/policies.go +++ b/server/datastore/mysql/policies.go @@ -1171,7 +1171,6 @@ func (ds *Datastore) GetCalendarPolicies(ctx context.Context, teamID uint) ([]fl return policies, nil } -// TODO(lucas): Must be tested at scale. func (ds *Datastore) GetTeamHostsPolicyMemberships( ctx context.Context, domain string, diff --git a/server/fleet/datastore.go b/server/fleet/datastore.go index 7d0b112a42..1c03aca415 100644 --- a/server/fleet/datastore.go +++ b/server/fleet/datastore.go @@ -594,6 +594,11 @@ type Datastore interface { PolicyQueriesForHost(ctx context.Context, host *Host) (map[string]string, error) + // GetTeamHostsPolicyMembmerships returns the hosts that belong to the given team and their pass/fail statuses + // around the provided policyIDs. + // - Returns hosts of the team that are failing one or more of the provided policies. + // - Returns hosts of the team that are passing all the policies (or are not running any of the provided policies) + // and have a calendar event scheduled. GetTeamHostsPolicyMemberships(ctx context.Context, domain string, teamID uint, policyIDs []uint) ([]HostPolicyMembershipData, error) GetCalendarPolicies(ctx context.Context, teamID uint) ([]PolicyCalendarData, error) diff --git a/server/service/integration_enterprise_test.go b/server/service/integration_enterprise_test.go index 70891e6a23..06d2e1e388 100644 --- a/server/service/integration_enterprise_test.go +++ b/server/service/integration_enterprise_test.go @@ -16,18 +16,21 @@ import ( "sort" "strconv" "strings" + "sync" "testing" "time" - "github.com/fleetdm/fleet/v4/server/pubsub" - + "github.com/fleetdm/fleet/v4/ee/server/calendar" "github.com/fleetdm/fleet/v4/pkg/optjson" + "github.com/fleetdm/fleet/v4/server/cron" "github.com/fleetdm/fleet/v4/server/datastore/mysql" "github.com/fleetdm/fleet/v4/server/datastore/redis/redistest" "github.com/fleetdm/fleet/v4/server/fleet" "github.com/fleetdm/fleet/v4/server/live_query/live_query_mock" "github.com/fleetdm/fleet/v4/server/mdm" "github.com/fleetdm/fleet/v4/server/ptr" + "github.com/fleetdm/fleet/v4/server/pubsub" + "github.com/fleetdm/fleet/v4/server/service/schedule" "github.com/fleetdm/fleet/v4/server/test" kitlog "github.com/go-kit/kit/log" "github.com/go-kit/log" @@ -48,7 +51,8 @@ func TestIntegrationsEnterprise(t *testing.T) { type integrationEnterpriseTestSuite struct { withServer suite.Suite - redisPool fleet.RedisPool + redisPool fleet.RedisPool + calendarSchedule *schedule.Schedule lq *live_query_mock.MockLiveQuery } @@ -58,6 +62,7 @@ func (s *integrationEnterpriseTestSuite) SetupSuite() { s.redisPool = redistest.SetupRedis(s.T(), "integration_enterprise", false, false, false) s.lq = live_query_mock.New(s.T()) + var calendarSchedule *schedule.Schedule config := TestServerOpts{ License: &fleet.LicenseInfo{ Tier: fleet.TierPremium, @@ -67,6 +72,16 @@ func (s *integrationEnterpriseTestSuite) SetupSuite() { Lq: s.lq, Logger: log.NewLogfmtLogger(os.Stdout), EnableCachedDS: true, + StartCronSchedules: []TestNewScheduleFunc{ + func(ctx context.Context, ds fleet.Datastore) fleet.NewCronScheduleFunc { + return func() (fleet.CronSchedule, error) { + // We set 24-hour interval so that it only runs when triggered. + var err error + calendarSchedule, err = cron.NewCalendarSchedule(ctx, s.T().Name(), s.ds, 24*time.Hour, log.NewJSONLogger(os.Stdout)) + return calendarSchedule, err + } + }, + }, } if os.Getenv("FLEET_INTEGRATION_TESTS_DISABLE_LOG") != "" { config.Logger = kitlog.NewNopLogger() @@ -76,6 +91,7 @@ func (s *integrationEnterpriseTestSuite) SetupSuite() { s.users = users s.token = s.getTestAdminToken() s.cachedTokens = make(map[string]string) + s.calendarSchedule = calendarSchedule } func (s *integrationEnterpriseTestSuite) TearDownTest() { @@ -3605,7 +3621,6 @@ func (s *integrationEnterpriseTestSuite) TestOSVersions() { "GET", fmt.Sprintf("/api/latest/fleet/os_versions/%d", osinfo.OSVersionID), nil, http.StatusForbidden, &osVersionResp, "team_id", "99999", ) - } func (s *integrationEnterpriseTestSuite) TestMDMNotConfiguredEndpoints() { @@ -7336,7 +7351,8 @@ func (s *integrationEnterpriseTestSuite) TestSoftwareAuth() { Description: "desc team1", }) require.NoError(t, err) - require.NoError(t, s.ds.AddHostsToTeam(ctx, &team1.ID, []uint{tmHost.ID})) + err = s.ds.AddHostsToTeam(ctx, &team1.ID, []uint{tmHost.ID}) + require.NoError(t, err) team2, err := s.ds.NewTeam(ctx, &fleet.Team{ ID: 43, Name: "team2", @@ -7653,3 +7669,653 @@ func (s *integrationEnterpriseTestSuite) TestSoftwareAuth() { // set the admin token again to avoid breaking other tests s.token = s.getTestAdminToken() } + +func (s *integrationEnterpriseTestSuite) TestCalendarEvents() { + ctx := context.Background() + t := s.T() + t.Cleanup(func() { + calendar.ClearMockEvents() + }) + currentAppCfg, err := s.ds.AppConfig(ctx) + require.NoError(t, err) + t.Cleanup(func() { + err = s.ds.SaveAppConfig(ctx, currentAppCfg) + require.NoError(t, err) + }) + + team1, err := s.ds.NewTeam(ctx, &fleet.Team{ + Name: "team1", + }) + require.NoError(t, err) + team2, err := s.ds.NewTeam(ctx, &fleet.Team{ + Name: "team2", + }) + require.NoError(t, err) + + newHost := func(name string, teamID *uint) *fleet.Host { + h, err := s.ds.NewHost(ctx, &fleet.Host{ + DetailUpdatedAt: time.Now(), + LabelUpdatedAt: time.Now(), + PolicyUpdatedAt: time.Now(), + SeenTime: time.Now().Add(-1 * time.Minute), + OsqueryHostID: ptr.String(t.Name() + name), + NodeKey: ptr.String(t.Name() + name), + UUID: uuid.New().String(), + Hostname: fmt.Sprintf("%s.%s.local", name, t.Name()), + Platform: "darwin", + TeamID: teamID, + }) + require.NoError(t, err) + return h + } + + host1Team1 := newHost("host1", &team1.ID) + host2Team1 := newHost("host2", &team1.ID) + host3Team2 := newHost("host3", &team2.ID) + host4Team2 := newHost("host4", &team2.ID) + _ = newHost("host5", nil) // global host + + team1Policy1Calendar, err := s.ds.NewTeamPolicy( + ctx, team1.ID, nil, fleet.PolicyPayload{ + Name: "team1Policy1Calendar", + Query: "SELECT 1;", + CalendarEventsEnabled: true, + }, + ) + require.NoError(t, err) + team1Policy2, err := s.ds.NewTeamPolicy( + ctx, team1.ID, nil, fleet.PolicyPayload{ + Name: "team1Policy2", + Query: "SELECT 2;", + CalendarEventsEnabled: true, + }, + ) + require.NoError(t, err) + team2Policy1Calendar, err := s.ds.NewTeamPolicy( + ctx, team1.ID, nil, fleet.PolicyPayload{ + Name: "team2Policy1Calendar", + Query: "SELECT 3;", + CalendarEventsEnabled: true, + }, + ) + require.NoError(t, err) + team2Policy2, err := s.ds.NewTeamPolicy( + ctx, team1.ID, nil, fleet.PolicyPayload{ + Name: "team2Policy2", + Query: "SELECT 4;", + CalendarEventsEnabled: false, + }, + ) + require.NoError(t, err) + globalPolicy, err := s.ds.NewGlobalPolicy( + ctx, nil, fleet.PolicyPayload{ + Name: "globalPolicy", + Query: "SELECT 5;", + CalendarEventsEnabled: false, + }, + ) + require.NoError(t, err) + + genDistributedReqWithPolicyResults := func(host *fleet.Host, policyResults map[uint]*bool) submitDistributedQueryResultsRequestShim { + var ( + results = make(map[string]json.RawMessage) + statuses = make(map[string]interface{}) + messages = make(map[string]string) + ) + for policyID, policyResult := range policyResults { + distributedQueryName := hostPolicyQueryPrefix + fmt.Sprint(policyID) + switch { + case policyResult == nil: + results[distributedQueryName] = json.RawMessage(`[]`) + statuses[distributedQueryName] = 1 + messages[distributedQueryName] = "policy failed execution" + case *policyResult: + results[distributedQueryName] = json.RawMessage(`[{"1": "1"}]`) + statuses[distributedQueryName] = 0 + case !*policyResult: + results[distributedQueryName] = json.RawMessage(`[]`) + statuses[distributedQueryName] = 0 + } + } + return submitDistributedQueryResultsRequestShim{ + NodeKey: *host.NodeKey, + Results: results, + Statuses: statuses, + Messages: messages, + Stats: map[string]*fleet.Stats{}, + } + } + + // host1Team1 is failing a calendar policy and not a non-calendar policy (no results for global). + distributedResp := submitDistributedQueryResultsResponse{} + s.DoJSON("POST", "/api/osquery/distributed/write", genDistributedReqWithPolicyResults( + host1Team1, + map[uint]*bool{ + team1Policy1Calendar.ID: ptr.Bool(false), + team1Policy2.ID: ptr.Bool(true), + globalPolicy.ID: nil, + }, + ), http.StatusOK, &distributedResp) + + // host2Team1 is passing the calendar policy but not the non-calendar policy (no results for global). + s.DoJSON("POST", "/api/osquery/distributed/write", genDistributedReqWithPolicyResults( + host2Team1, + map[uint]*bool{ + team2Policy1Calendar.ID: ptr.Bool(true), + team2Policy2.ID: ptr.Bool(false), + globalPolicy.ID: nil, + }, + ), http.StatusOK, &distributedResp) + + // host3Team2 is passing team2Policy1Calendar and failing the global policy + // (not results for team2Policy2). + s.DoJSON("POST", "/api/osquery/distributed/write", genDistributedReqWithPolicyResults( + host3Team2, + map[uint]*bool{ + team2Policy1Calendar.ID: ptr.Bool(true), + team2Policy2.ID: nil, + globalPolicy.ID: ptr.Bool(false), + }, + ), http.StatusOK, &distributedResp) + + // host4Team2 is not returning results for the calendar policy, failing the non-calendar + // policy and passing the global policy. + s.DoJSON("POST", "/api/osquery/distributed/write", genDistributedReqWithPolicyResults( + host4Team2, + map[uint]*bool{ + team2Policy1Calendar.ID: nil, + team2Policy2.ID: ptr.Bool(false), + globalPolicy.ID: ptr.Bool(true), + }, + ), http.StatusOK, &distributedResp) + + // Trigger the calendar cron with the global feature is disabled. + triggerAndWait(ctx, t, s.ds, s.calendarSchedule, 5*time.Second) + + // No calendar events were created. + allCalendarEvents, err := s.ds.ListCalendarEvents(ctx, nil) + require.NoError(t, err) + require.Empty(t, allCalendarEvents) + + // Set global configuration for the calendar feature. + appCfg, err := s.ds.AppConfig(ctx) + require.NoError(t, err) + appCfg.Integrations.GoogleCalendar = []*fleet.GoogleCalendarIntegration{ + { + Domain: "example.com", + ApiKey: map[string]string{ + fleet.GoogleCalendarEmail: "calendar-mock@example.com", + }, + }, + } + err = s.ds.SaveAppConfig(ctx, appCfg) + require.NoError(t, err) + time.Sleep(2 * time.Second) // Wait 2 seconds for the app config cache to clear. + + triggerAndWait(ctx, t, s.ds, s.calendarSchedule, 5*time.Second) + + // No calendar events were created because we are missing enabling it on the teams. + allCalendarEvents, err = s.ds.ListCalendarEvents(ctx, nil) + require.NoError(t, err) + require.Empty(t, allCalendarEvents) + + // Run distributed/write for host4Team2 again, it should not attempt to trigger the webhook because + // it's disabled for the teams. + s.DoJSON("POST", "/api/osquery/distributed/write", genDistributedReqWithPolicyResults( + host4Team2, + map[uint]*bool{ + team2Policy1Calendar.ID: nil, + team2Policy2.ID: ptr.Bool(false), + globalPolicy.ID: ptr.Bool(true), + }, + ), http.StatusOK, &distributedResp) + + var ( + team1Fired int + team1FiredMu sync.Mutex + ) + + team1WebhookFired := make(chan struct{}) + team1WebhookServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + require.Equal(t, "POST", r.Method) + requestBodyBytes, err := io.ReadAll(r.Body) + require.NoError(t, err) + t.Logf("team1 webhook request: %s\n", requestBodyBytes) + team1FiredMu.Lock() + team1Fired++ + team1WebhookFired <- struct{}{} + team1FiredMu.Unlock() + })) + t.Cleanup(func() { + team1WebhookServer.Close() + }) + + team1.Config.Integrations.GoogleCalendar = &fleet.TeamGoogleCalendarIntegration{ + Enable: true, + WebhookURL: team1WebhookServer.URL, + } + team1, err = s.ds.SaveTeam(ctx, team1) + require.NoError(t, err) + + var ( + team2Fired int + team2FiredMu sync.Mutex + ) + + team2WebhookServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + require.Equal(t, "POST", r.Method) + requestBodyBytes, err := io.ReadAll(r.Body) + require.NoError(t, err) + t.Logf("team2 webhook request: %s\n", requestBodyBytes) + team2FiredMu.Lock() + team2Fired++ + team2FiredMu.Unlock() + })) + t.Cleanup(func() { + team2WebhookServer.Close() + }) + + team2.Config.Integrations.GoogleCalendar = &fleet.TeamGoogleCalendarIntegration{ + Enable: true, + WebhookURL: team2WebhookServer.URL, + } + team2, err = s.ds.SaveTeam(ctx, team2) + require.NoError(t, err) + + // + // Same distributed/write as before but they should not fire yet. + // + + // host1Team1 is failing a calendar policy and not a non-calendar policy (no results for global). + s.DoJSON("POST", "/api/osquery/distributed/write", genDistributedReqWithPolicyResults( + host1Team1, + map[uint]*bool{ + team1Policy1Calendar.ID: ptr.Bool(false), + team1Policy2.ID: ptr.Bool(true), + globalPolicy.ID: nil, + }, + ), http.StatusOK, &distributedResp) + + // host2Team1 is passing the calendar policy but not the non-calendar policy (no results for global). + s.DoJSON("POST", "/api/osquery/distributed/write", genDistributedReqWithPolicyResults( + host2Team1, + map[uint]*bool{ + team2Policy1Calendar.ID: ptr.Bool(true), + team2Policy2.ID: ptr.Bool(false), + globalPolicy.ID: nil, + }, + ), http.StatusOK, &distributedResp) + + // host3Team2 is passing team2Policy1Calendar and failing the global policy + // (not results for team2Policy2). + s.DoJSON("POST", "/api/osquery/distributed/write", genDistributedReqWithPolicyResults( + host3Team2, + map[uint]*bool{ + team2Policy1Calendar.ID: ptr.Bool(true), + team2Policy2.ID: nil, + globalPolicy.ID: ptr.Bool(false), + }, + ), http.StatusOK, &distributedResp) + + // host4Team2 is not returning results for the calendar policy, failing the non-calendar + // policy and passing the global policy. + s.DoJSON("POST", "/api/osquery/distributed/write", genDistributedReqWithPolicyResults( + host4Team2, + map[uint]*bool{ + team2Policy1Calendar.ID: nil, + team2Policy2.ID: ptr.Bool(false), + globalPolicy.ID: ptr.Bool(true), + }, + ), http.StatusOK, &distributedResp) + + team1FiredMu.Lock() + require.Zero(t, team1Fired) + team1FiredMu.Unlock() + + team2FiredMu.Lock() + require.Zero(t, team2Fired) + team2FiredMu.Unlock() + + // Trigger the calendar cron, global feature enabled, team1 enabled, team2 not yet enabled + // and hosts do not have an associated email yet. + triggerAndWait(ctx, t, s.ds, s.calendarSchedule, 5*time.Second) + + team1CalendarEvents, err := s.ds.ListCalendarEvents(ctx, &team1.ID) + require.NoError(t, err) + require.Empty(t, team1CalendarEvents) + + // Add an email but of another domain. + err = s.ds.ReplaceHostDeviceMapping(ctx, host1Team1.ID, []*fleet.HostDeviceMapping{ + { + HostID: host1Team1.ID, + Email: "user@other.com", + Source: "google_chrome_profiles", + }, + }, "google_chrome_profiles") + require.NoError(t, err) + + // Trigger the calendar cron, global feature enabled, team1 enabled, team2 not yet enabled + // and hosts do not have an associated email for the domain yet. + triggerAndWait(ctx, t, s.ds, s.calendarSchedule, 5*time.Second) + + team1CalendarEvents, err = s.ds.ListCalendarEvents(ctx, &team1.ID) + require.NoError(t, err) + require.Empty(t, team1CalendarEvents) + + err = s.ds.ReplaceHostDeviceMapping(ctx, host1Team1.ID, []*fleet.HostDeviceMapping{ + { + HostID: host1Team1.ID, + Email: "user1@example.com", + Source: "google_chrome_profiles", + }, + }, "google_chrome_profiles") + require.NoError(t, err) + + // Trigger the calendar cron, global feature enabled, team1 enabled, team2 not yet enabled + // and host1Team1 has a domain email associated. + triggerAndWait(ctx, t, s.ds, s.calendarSchedule, 5*time.Second) + + // An event should be generated for host1Team1 + team1CalendarEvents, err = s.ds.ListCalendarEvents(ctx, &team1.ID) + require.NoError(t, err) + require.Len(t, team1CalendarEvents, 1) + require.NotZero(t, team1CalendarEvents[0].ID) + require.Equal(t, "user1@example.com", team1CalendarEvents[0].Email) + require.NotZero(t, team1CalendarEvents[0].StartTime) + require.NotZero(t, team1CalendarEvents[0].EndTime) + + calendar.SetMockEventsToNow() + + mysql.ExecAdhocSQL(t, s.ds, func(db sqlx.ExtContext) error { + // Update updated_at so the event gets updated (the event is updated every 30 minutes) + _, err := db.ExecContext(ctx, + `UPDATE calendar_events SET updated_at = DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 1 HOUR) WHERE id = ?`, team1CalendarEvents[0].ID) + if err != nil { + return err + } + // Set host1Team1 as online. + if _, err := db.ExecContext(ctx, + `UPDATE host_seen_times SET seen_time = CURRENT_TIMESTAMP WHERE host_id = ?`, host1Team1.ID); err != nil { + return err + } + return nil + }) + + // Trigger the calendar cron, global feature enabled, team1 enabled, team2 not yet enabled + // and host1Team1 has a domain email associated. + triggerAndWait(ctx, t, s.ds, s.calendarSchedule, 5*time.Second) + + // Check that refetch on the host was set. + host, err := s.ds.Host(ctx, host1Team1.ID) + require.NoError(t, err) + require.True(t, host.RefetchRequested) + + // host1Team1 is failing a calendar policy and not a non-calendar policy (no results for global). + s.DoJSON("POST", "/api/osquery/distributed/write", genDistributedReqWithPolicyResults( + host1Team1, + map[uint]*bool{ + team1Policy1Calendar.ID: ptr.Bool(false), + team1Policy2.ID: ptr.Bool(true), + globalPolicy.ID: nil, + }, + ), http.StatusOK, &distributedResp) + + // host2Team1 is passing the calendar policy but not the non-calendar policy (no results for global). + s.DoJSON("POST", "/api/osquery/distributed/write", genDistributedReqWithPolicyResults( + host2Team1, + map[uint]*bool{ + team2Policy1Calendar.ID: ptr.Bool(true), + team2Policy2.ID: ptr.Bool(false), + globalPolicy.ID: nil, + }, + ), http.StatusOK, &distributedResp) + + select { + case <-team1WebhookFired: + case <-time.After(5 * time.Second): + t.Error("timeout waiting for team1 webhook to fire") + } + + // Trigger again, nothing should fire as webhook has already fired. + triggerAndWait(ctx, t, s.ds, s.calendarSchedule, 5*time.Second) + + team1FiredMu.Lock() + require.Equal(t, 1, team1Fired) + team1FiredMu.Unlock() + team2FiredMu.Lock() + require.Equal(t, 0, team2Fired) + team2FiredMu.Unlock() + + // Make host1Team1 pass all policies. + s.DoJSON("POST", "/api/osquery/distributed/write", genDistributedReqWithPolicyResults( + host1Team1, + map[uint]*bool{ + team1Policy1Calendar.ID: ptr.Bool(true), + team1Policy2.ID: ptr.Bool(true), + globalPolicy.ID: nil, + }, + ), http.StatusOK, &distributedResp) + + // Trigger calendar should cleanup the events. + triggerAndWait(ctx, t, s.ds, s.calendarSchedule, 5*time.Second) + + // Events in the user calendar should not be cleaned up because they are not in the future. + mockEvents := calendar.ListGoogleMockEvents() + require.NotEmpty(t, mockEvents) + + // Event should be cleaned up from our database. + team1CalendarEvents, err = s.ds.ListCalendarEvents(ctx, &team1.ID) + require.NoError(t, err) + require.Empty(t, team1CalendarEvents) +} + +func (s *integrationEnterpriseTestSuite) TestCalendarEventsTransferringHosts() { + ctx := context.Background() + t := s.T() + t.Cleanup(func() { + calendar.ClearMockEvents() + }) + currentAppCfg, err := s.ds.AppConfig(ctx) + require.NoError(t, err) + t.Cleanup(func() { + err = s.ds.SaveAppConfig(ctx, currentAppCfg) + require.NoError(t, err) + }) + + // Set global configuration for the calendar feature. + appCfg, err := s.ds.AppConfig(ctx) + require.NoError(t, err) + appCfg.Integrations.GoogleCalendar = []*fleet.GoogleCalendarIntegration{ + { + Domain: "example.com", + ApiKey: map[string]string{ + fleet.GoogleCalendarEmail: "calendar-mock@example.com", + }, + }, + } + err = s.ds.SaveAppConfig(ctx, appCfg) + require.NoError(t, err) + time.Sleep(2 * time.Second) // Wait 2 seconds for the app config cache to clear. + + team1, err := s.ds.NewTeam(ctx, &fleet.Team{ + Name: "team1", + }) + require.NoError(t, err) + team2, err := s.ds.NewTeam(ctx, &fleet.Team{ + Name: "team2", + }) + require.NoError(t, err) + + team1.Config.Integrations.GoogleCalendar = &fleet.TeamGoogleCalendarIntegration{ + Enable: true, + WebhookURL: "https://foo.example.com", + } + team1, err = s.ds.SaveTeam(ctx, team1) + require.NoError(t, err) + team2.Config.Integrations.GoogleCalendar = &fleet.TeamGoogleCalendarIntegration{ + Enable: true, + WebhookURL: "https://foo.example.com", + } + team2, err = s.ds.SaveTeam(ctx, team2) + require.NoError(t, err) + + newHost := func(name string, teamID *uint) *fleet.Host { + h, err := s.ds.NewHost(ctx, &fleet.Host{ + DetailUpdatedAt: time.Now(), + LabelUpdatedAt: time.Now(), + PolicyUpdatedAt: time.Now(), + SeenTime: time.Now().Add(-1 * time.Minute), + OsqueryHostID: ptr.String(t.Name() + name), + NodeKey: ptr.String(t.Name() + name), + UUID: uuid.New().String(), + Hostname: fmt.Sprintf("%s.%s.local", name, t.Name()), + Platform: "darwin", + TeamID: teamID, + }) + require.NoError(t, err) + return h + } + + host1 := newHost("host1", &team1.ID) + err = s.ds.ReplaceHostDeviceMapping(ctx, host1.ID, []*fleet.HostDeviceMapping{ + { + HostID: host1.ID, + Email: "user1@example.com", + Source: "google_chrome_profiles", + }, + }, "google_chrome_profiles") + require.NoError(t, err) + + team1Policy1, err := s.ds.NewTeamPolicy( + ctx, team1.ID, nil, fleet.PolicyPayload{ + Name: "team1Policy1", + Query: "SELECT 1;", + CalendarEventsEnabled: true, + }, + ) + require.NoError(t, err) + team2Policy1, err := s.ds.NewTeamPolicy( + ctx, team2.ID, nil, fleet.PolicyPayload{ + Name: "team2Policy1", + Query: "SELECT 2;", + CalendarEventsEnabled: true, + }, + ) + require.NoError(t, err) + + distributedResp := submitDistributedQueryResultsResponse{} + s.DoJSON("POST", "/api/osquery/distributed/write", genDistributedReqWithPolicyResults( + host1, + map[uint]*bool{ + team1Policy1.ID: ptr.Bool(false), + }, + ), http.StatusOK, &distributedResp) + + triggerAndWait(ctx, t, s.ds, s.calendarSchedule, 5*time.Second) + + team1CalendarEvents, err := s.ds.ListCalendarEvents(ctx, &team1.ID) + require.NoError(t, err) + require.Len(t, team1CalendarEvents, 1) + + // Check the calendar was created on the DB. + hostCalendarEvent, calendarEvent, err := s.ds.GetHostCalendarEventByEmail(ctx, "user1@example.com") + require.NoError(t, err) + + // Transfer host to team2. + err = s.ds.AddHostsToTeam(ctx, &team2.ID, []uint{host1.ID}) + require.NoError(t, err) + + // host1 is failing team2's policy too. + s.DoJSON("POST", "/api/osquery/distributed/write", genDistributedReqWithPolicyResults( + host1, + map[uint]*bool{ + team2Policy1.ID: ptr.Bool(false), + }, + ), http.StatusOK, &distributedResp) + + triggerAndWait(ctx, t, s.ds, s.calendarSchedule, 5*time.Second) + + // Check the calendar event entry was reused. + hostCalendarEvent2, calendarEvent2, err := s.ds.GetHostCalendarEventByEmail(ctx, "user1@example.com") + require.NoError(t, err) + require.Equal(t, calendarEvent2.ID, calendarEvent.ID) + require.Equal(t, hostCalendarEvent2.CalendarEventID, hostCalendarEvent.CalendarEventID) + + // Transfer host to global. + err = s.ds.AddHostsToTeam(ctx, nil, []uint{host1.ID}) + require.NoError(t, err) + + // Move event to two days ago (to clean up the calendar event) + mysql.ExecAdhocSQL(t, s.ds, func(db sqlx.ExtContext) error { + _, err := db.ExecContext(ctx, + `UPDATE calendar_events SET updated_at = DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 49 HOUR) WHERE id = ?`, team1CalendarEvents[0].ID) + if err != nil { + return err + } + return nil + }) + + triggerAndWait(ctx, t, s.ds, s.calendarSchedule, 5*time.Second) + + // Calendar event is cleaned up. + _, _, err = s.ds.GetHostCalendarEventByEmail(ctx, "user1@example.com") + require.True(t, fleet.IsNotFound(err)) +} + +func genDistributedReqWithPolicyResults(host *fleet.Host, policyResults map[uint]*bool) submitDistributedQueryResultsRequestShim { + var ( + results = make(map[string]json.RawMessage) + statuses = make(map[string]interface{}) + messages = make(map[string]string) + ) + for policyID, policyResult := range policyResults { + distributedQueryName := hostPolicyQueryPrefix + fmt.Sprint(policyID) + switch { + case policyResult == nil: + results[distributedQueryName] = json.RawMessage(`[]`) + statuses[distributedQueryName] = 1 + messages[distributedQueryName] = "policy failed execution" + case *policyResult: + results[distributedQueryName] = json.RawMessage(`[{"1": "1"}]`) + statuses[distributedQueryName] = 0 + case !*policyResult: + results[distributedQueryName] = json.RawMessage(`[]`) + statuses[distributedQueryName] = 0 + } + } + return submitDistributedQueryResultsRequestShim{ + NodeKey: *host.NodeKey, + Results: results, + Statuses: statuses, + Messages: messages, + Stats: map[string]*fleet.Stats{}, + } +} + +func triggerAndWait(ctx context.Context, t *testing.T, ds fleet.Datastore, s *schedule.Schedule, timeout time.Duration) { + // Following code assumes (for simplicity) only triggered runs. + stats, err := ds.GetLatestCronStats(ctx, s.Name()) + require.NoError(t, err) + var previousRunID int + if len(stats) > 0 { + previousRunID = stats[0].ID + } + + _, err = s.Trigger() + require.NoError(t, err) + + timeoutCh := time.After(timeout) + for { + stats, err := ds.GetLatestCronStats(ctx, s.Name()) + require.NoError(t, err) + if len(stats) > 0 && stats[0].ID > previousRunID && stats[0].Status == fleet.CronStatsStatusCompleted { + t.Logf("cron %s:%d done", s.Name(), stats[0].ID) + return + } + select { + case <-timeoutCh: + t.Fatalf("timeout waiting for schedule %s to complete", s.Name()) + case <-time.After(250 * time.Millisecond): + } + } +} diff --git a/server/service/schedule/schedule.go b/server/service/schedule/schedule.go index eabe66c394..2d0bacb645 100644 --- a/server/service/schedule/schedule.go +++ b/server/service/schedule/schedule.go @@ -162,7 +162,7 @@ func New( // // All jobs must be added before calling Start. func (s *Schedule) Start() { - prevScheduledRun, _, err := s.getLatestStats() + prevScheduledRun, _, err := s.GetLatestStats() if err != nil { level.Error(s.logger).Log("err", "start schedule", "details", err) ctxerr.Handle(s.ctx, err) @@ -203,7 +203,7 @@ func (s *Schedule) Start() { s.runWithStats(fleet.CronStatsTypeTriggered) - prevScheduledRun, _, err := s.getLatestStats() + prevScheduledRun, _, err := s.GetLatestStats() if err != nil { level.Error(s.logger).Log("err", "trigger get cron stats", "details", err) ctxerr.Handle(s.ctx, err) @@ -235,7 +235,7 @@ func (s *Schedule) Start() { schedInterval := s.getSchedInterval() - prevScheduledRun, prevTriggeredRun, err := s.getLatestStats() + prevScheduledRun, prevTriggeredRun, err := s.GetLatestStats() if err != nil { level.Error(s.logger).Log("err", "get cron stats", "details", err) ctxerr.Handle(s.ctx, err) @@ -374,7 +374,7 @@ func (s *Schedule) Start() { // is blocked or otherwise unavailable to publish the signal. From the caller's perspective, both // cases are deemed to be equivalent. func (s *Schedule) Trigger() (*fleet.CronStats, error) { - sched, trig, err := s.getLatestStats() + sched, trig, err := s.GetLatestStats() switch { case err != nil: return nil, err @@ -549,7 +549,7 @@ func (s *Schedule) holdLock() (bool, context.CancelFunc) { return true, cancelFn } -func (s *Schedule) getLatestStats() (fleet.CronStats, fleet.CronStats, error) { +func (s *Schedule) GetLatestStats() (fleet.CronStats, fleet.CronStats, error) { var scheduled, triggered fleet.CronStats cs, err := s.statsStore.GetLatestCronStats(s.ctx, s.name) diff --git a/server/service/testing_client.go b/server/service/testing_client.go index 6445b6dbee..5b67ada9ee 100644 --- a/server/service/testing_client.go +++ b/server/service/testing_client.go @@ -95,6 +95,12 @@ func (ts *withServer) TearDownSuite() { } func (ts *withServer) commonTearDownTest(t *testing.T) { + // By setting DISABLE_TABLES_CLEANUP a developer can troubleshoot tests + // by inspecting mysql tables. + if os.Getenv("DISABLE_CLEANUP_TABLES") != "" { + return + } + ctx := context.Background() u := ts.users["admin1@example.com"] From 8194459ee9d60e755347e2c8a3a3488d7cb48118 Mon Sep 17 00:00:00 2001 From: Lucas Manuel Rodriguez Date: Thu, 4 Apr 2024 15:24:16 -0300 Subject: [PATCH 28/60] Use osqueryd edge on servers canary team (#18058) I copied `it-and-security/lib/servers.agent-options.yml` and added the `update_channels` key. --- .../lib/servers-canary.agent-options.yml | 16 ++++++++++++++++ it-and-security/teams/servers-canary.yml | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 it-and-security/lib/servers-canary.agent-options.yml diff --git a/it-and-security/lib/servers-canary.agent-options.yml b/it-and-security/lib/servers-canary.agent-options.yml new file mode 100644 index 0000000000..731e956121 --- /dev/null +++ b/it-and-security/lib/servers-canary.agent-options.yml @@ -0,0 +1,16 @@ +config: + decorators: + load: + - SELECT uuid AS host_uuid FROM system_info; + - SELECT hostname AS hostname FROM system_info; + options: + disable_distributed: false + distributed_interval: 10 + distributed_plugin: tls + distributed_tls_max_attempts: 3 + logger_tls_endpoint: /api/osquery/log + logger_tls_period: 10 + pack_delimiter: / +update_channels: + # We want to use these hosts to smoke test osquery releases. + osqueryd: edge diff --git a/it-and-security/teams/servers-canary.yml b/it-and-security/teams/servers-canary.yml index fe582aede3..d87ea5bcba 100644 --- a/it-and-security/teams/servers-canary.yml +++ b/it-and-security/teams/servers-canary.yml @@ -9,7 +9,7 @@ team_settings: secrets: - secret: $DOGFOOD_SERVERS_CANARY_ENROLL_SECRET agent_options: - path: ../lib/servers.agent-options.yml + path: ../lib/servers-canary.agent-options.yml controls: enable_disk_encryption: false macos_settings: From 4585a6e2c29d5bd0afb6c04a8c40fa29ad2bdfb1 Mon Sep 17 00:00:00 2001 From: Marko Lisica <83164494+marko-lisica@users.noreply.github.com> Date: Thu, 4 Apr 2024 20:38:42 +0200 Subject: [PATCH 29/60] API design: Pre-fill and lock local account creation screen during out-of-the-box macOS setup (#17118) API design for: - #9147 --- docs/REST API/rest-api.md | 3 ++- tools/mdm/apple/dep_sample_profile.json | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/REST API/rest-api.md b/docs/REST API/rest-api.md index 68219d49d7..4d7de79712 100644 --- a/docs/REST API/rest-api.md +++ b/docs/REST API/rest-api.md @@ -906,7 +906,8 @@ None. "macos_setup": { "bootstrap_package": "", "enable_end_user_authentication": false, - "macos_setup_assistant": "path/to/config.json" + "macos_setup_assistant": "path/to/config.json", + "enable_release_device_manually": true } }, "agent_options": { diff --git a/tools/mdm/apple/dep_sample_profile.json b/tools/mdm/apple/dep_sample_profile.json index a653bda5da..af76650f54 100644 --- a/tools/mdm/apple/dep_sample_profile.json +++ b/tools/mdm/apple/dep_sample_profile.json @@ -2,7 +2,6 @@ "profile_name": "Fleet Device Management Inc.", "allow_pairing": true, "auto_advance_setup": false, - "await_device_configured": false, "department": "it@fleetdm.com", "is_supervised": false, "is_multi_user": false, From b7296ce4f9b7f4ed4819e8922896ff2757c9d7f1 Mon Sep 17 00:00:00 2001 From: RachelElysia <71795832+RachelElysia@users.noreply.github.com> Date: Thu, 4 Apr 2024 15:18:04 -0400 Subject: [PATCH 30/60] Fleet UI: Surface orbit version and fleetd version to host details page (#18013) --- changes/17361-host-details-updates | 1 + frontend/__mocks__/hostMock.ts | 6 +++- frontend/interfaces/host.ts | 4 +++ .../HostDetailsPage/HostDetailsPage.tsx | 1 - .../details/cards/HostSummary/HostSummary.tsx | 36 ++++++++++++++++++- frontend/utilities/constants.tsx | 2 ++ 6 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 changes/17361-host-details-updates diff --git a/changes/17361-host-details-updates b/changes/17361-host-details-updates new file mode 100644 index 0000000000..7f3235f2af --- /dev/null +++ b/changes/17361-host-details-updates @@ -0,0 +1 @@ +- UI: Surface fleet desktop and orbit version to the host details page diff --git a/frontend/__mocks__/hostMock.ts b/frontend/__mocks__/hostMock.ts index 5912ef451c..0aaf080bba 100644 --- a/frontend/__mocks__/hostMock.ts +++ b/frontend/__mocks__/hostMock.ts @@ -1,4 +1,4 @@ -import { IHost } from "interfaces/host"; +import { IHost, IHostResponse } from "interfaces/host"; import { IHostMdmProfile } from "interfaces/mdm"; const DEFAULT_HOST_PROFILE_MOCK: IHostMdmProfile = { @@ -34,6 +34,8 @@ const DEFAULT_HOST_MOCK: IHost = { uuid: "09b244f8-0000-0000-b5cc-791a15f11073", platform: "ubuntu", osquery_version: "4.9.0", + orbit_version: "1.22.0", + fleet_desktop_version: "1.22.0", os_version: "Ubuntu 18.4.0", build: "", platform_like: "debian", @@ -101,4 +103,6 @@ const createMockHost = (overrides?: Partial): IHost => { return { ...DEFAULT_HOST_MOCK, ...overrides }; }; +export const createMockHostResponse = { host: createMockHost() }; + export default createMockHost; diff --git a/frontend/interfaces/host.ts b/frontend/interfaces/host.ts index 25fc4ab2f0..2e92da6490 100644 --- a/frontend/interfaces/host.ts +++ b/frontend/interfaces/host.ts @@ -29,6 +29,8 @@ export default PropTypes.shape({ uuid: PropTypes.string, platform: PropTypes.string, osquery_version: PropTypes.string, + orbit_version: PropTypes.string, + fleet_desktop_version: PropTypes.string, os_version: PropTypes.string, build: PropTypes.string, platform_like: PropTypes.string, @@ -267,6 +269,8 @@ export interface IHost { uuid: string; platform: string; osquery_version: string; + orbit_version?: string; + fleet_desktop_version?: string; os_version: string; build: string; platform_like: string; // TODO: replace with more specific union type diff --git a/frontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsx b/frontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsx index dbab4b2288..9d07971117 100644 --- a/frontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsx +++ b/frontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsx @@ -52,7 +52,6 @@ import { HOST_ABOUT_DATA, HOST_OSQUERY_DATA, } from "utilities/constants"; -import { createMockHostMdmProfile } from "__mocks__/hostMock"; import Spinner from "components/Spinner"; import TabsWrapper from "components/TabsWrapper"; diff --git a/frontend/pages/hosts/details/cards/HostSummary/HostSummary.tsx b/frontend/pages/hosts/details/cards/HostSummary/HostSummary.tsx index 8a453d61e0..f52c8b06bc 100644 --- a/frontend/pages/hosts/details/cards/HostSummary/HostSummary.tsx +++ b/frontend/pages/hosts/details/cards/HostSummary/HostSummary.tsx @@ -317,6 +317,39 @@ const HostSummary = ({ ); }; + const renderAgentSummary = () => { + if (platform === "chrome") { + return ; + } + if (summaryData.orbit_version) { + return ( + + osquery: {summaryData.osquery_version} +
+ Orbit: {summaryData.orbit_version} + {summaryData.fleet_desktop_version && ( + <> +
+ Fleet Desktop: {summaryData.fleet_desktop_version} + + )} + + } + > + {summaryData.orbit_version} + + } + /> + ); + } + return ; + }; + const renderSummary = () => { // for windows hosts we have to manually add a profile for disk encryption // as this is not currently included in the `profiles` value from the API @@ -401,7 +434,8 @@ const HostSummary = ({ /> - + + {renderAgentSummary()} ); }; diff --git a/frontend/utilities/constants.tsx b/frontend/utilities/constants.tsx index 0f20ff5d01..186dd39184 100644 --- a/frontend/utilities/constants.tsx +++ b/frontend/utilities/constants.tsx @@ -341,6 +341,8 @@ export const HOST_SUMMARY_DATA = [ "platform", "os_version", "osquery_version", + "orbit_version", + "fleet_desktop_version", "enroll_secret_name", "detail_updated_at", "percent_disk_space_available", From c1752c9cfdf323032db79f8e787b9ecb8fe2ddee Mon Sep 17 00:00:00 2001 From: Sam Pfluger <108141731+Sampfluger88@users.noreply.github.com> Date: Thu, 4 Apr 2024 15:06:15 -0500 Subject: [PATCH 31/60] Fix sentence case (#18069) --- handbook/company/communications.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/handbook/company/communications.md b/handbook/company/communications.md index ba37a9240a..007daa2b43 100644 --- a/handbook/company/communications.md +++ b/handbook/company/communications.md @@ -214,15 +214,15 @@ If you need to track content from a Slack channel (ie. #g-sales), you can automa **Fleet Free:** -| Impact Level | Definition | Preferred Contact | Response Time | +| Impact level | Definition | Preferred contact | Response time | |:---|:---|:---|:---| -| All Inquiries | Any request regardless of impact level or severity | Osquery #fleet Slack channel | No guaranteed resolution | +| All inquiries | Any request regardless of impact level or severity | Osquery #fleet Slack channel | No guaranteed resolution | > **Note:** If you're using Fleet Free, you can also access community support by [opening a bug](https://github.com/fleetdm/fleet/issues/new?assignees=&labels=bug%2C%3Areproduce&projects=&template=bug-report.md&title=) in the [Fleet GitHub](https://github.com/fleetdm/fleet/) repository. **Fleet Premium:** -| Impact Level | Definition | Preferred Contact | Response Time | +| Impact level | Definition | Preferred contact | Response time | |:-----|:----|:----|:-----| | Emergency (P0) | Your production instance of Fleet is unavailable or completely unusable. For example, if Fleet is showing 502 errors for all users. | Expedited phone/chat/email support during business hours.

Email the contact address provided in your Fleet contract or chat with us via your dedicated private Slack channel | **≀4 hours** | | High (P1) | Fleet is highly degraded with significant business impact. | Expedited phone/chat/email support during business hours.

Email the contact address provided in your Fleet contract or chat with us via your dedicated private Slack channel | **≀4 business hours** | From 842c258d7cd405081e1f9697da3314f39622541a Mon Sep 17 00:00:00 2001 From: Noah Talerman <47070608+noahtalerman@users.noreply.github.com> Date: Thu, 4 Apr 2024 16:54:16 -0400 Subject: [PATCH 32/60] Update workstations-canary.yml (#18051) - Remove "chromeOS/macOS - Screenlock enabled" --- it-and-security/teams/workstations-canary.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/it-and-security/teams/workstations-canary.yml b/it-and-security/teams/workstations-canary.yml index 22bb5fe7be..1ace8e43cf 100644 --- a/it-and-security/teams/workstations-canary.yml +++ b/it-and-security/teams/workstations-canary.yml @@ -100,12 +100,6 @@ policies: - path: ../lib/macos-device-health.policies.yml - path: ../lib/windows-device-health.policies.yml - path: ../lib/linux-device-health.policies.yml - - name: chromeOS/macOS - Screenlock enabled - query: SELECT 1 FROM screenlock WHERE enabled = 1; - critical: false - description: "" - resolution: "" - platform: darwin,chrome queries: - path: ../lib/collect-failed-login-attempts.queries.yml - path: ../lib/collect-usb-devices.queries.yml From 9bf9a162fccfaba8d4db0aaa26b8ef6e7200e7d5 Mon Sep 17 00:00:00 2001 From: Noah Talerman <47070608+noahtalerman@users.noreply.github.com> Date: Thu, 4 Apr 2024 16:55:25 -0400 Subject: [PATCH 33/60] Run script modal: Update copy (#18068) For the following story: #16460 --- .../HostDetailsPage/modals/RunScriptModal/RunScriptModal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/pages/hosts/details/HostDetailsPage/modals/RunScriptModal/RunScriptModal.tsx b/frontend/pages/hosts/details/HostDetailsPage/modals/RunScriptModal/RunScriptModal.tsx index fb2fb53b0f..0283841726 100644 --- a/frontend/pages/hosts/details/HostDetailsPage/modals/RunScriptModal/RunScriptModal.tsx +++ b/frontend/pages/hosts/details/HostDetailsPage/modals/RunScriptModal/RunScriptModal.tsx @@ -141,8 +141,8 @@ const RunScriptModal = ({ {!isLoading && isError && } {!isLoading && !isError && (!tableData || tableData.length === 0) && ( )} {!isLoading && !isError && tableData && tableData.length > 0 && ( From 8042c9cec4b4c884f337591e80f2f40b5552638c Mon Sep 17 00:00:00 2001 From: Noah Talerman <47070608+noahtalerman@users.noreply.github.com> Date: Thu, 4 Apr 2024 17:01:44 -0400 Subject: [PATCH 34/60] Head of Product Design is DRI for features table (#18052) --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 6813739818..320a4a4c5d 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -74,7 +74,7 @@ go.mod @fleetdm/go # # (see website/config/custom.js for DRIs of other paths not listed here) ############################################################################################## -/handbook/company/pricing-features-table.yml @mikermcneil # Β« CEO is current DRI for features table +/handbook/company/pricing-features-table.yml @noahtalerman # Β« Head of Product Design is current DRI for features table ############################################################################################## # 🦿 Repo automation and change control settings From a8fce3455a4672df86f9b6baa23ae8de20a0ac4d Mon Sep 17 00:00:00 2001 From: Isabell Reedy <113355639+ireedy@users.noreply.github.com> Date: Thu, 4 Apr 2024 17:02:25 -0400 Subject: [PATCH 35/60] Add Award to digital experience page (#18049) --- handbook/digital-experience/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/handbook/digital-experience/README.md b/handbook/digital-experience/README.md index 89280390c1..4463438ce7 100644 --- a/handbook/digital-experience/README.md +++ b/handbook/digital-experience/README.md @@ -8,6 +8,7 @@ This page details processes specific to working [with](#contact-us) and [within] | Head of Design | [Mike Thomas](https://www.linkedin.com/in/mike-thomas-52277938) _([@mike-j-thomas](https://github.com/mike-j-thomas))_ | Software Engineer | [Eric Shaw](https://www.linkedin.com/in/eric-shaw-1423831a9/) _([@eashaw](https://github.com/eashaw))_ | Head of Revenue Operations | [Taylor Hughes](https://www.linkedin.com/in/taylorhughes834/) _([@hughestaylor](https://github.com/hughestaylor))_ +| Apprentice | [Award Malisi](https://www.linkedin.com/in/award-malisi/) _([@Unearthlyglow](https://github.com/Unearthlyglow))_ ## Contact us From 068670d8e33ba1f3ec052f05340836b86d2830ad Mon Sep 17 00:00:00 2001 From: JD Date: Thu, 4 Apr 2024 14:17:17 -0700 Subject: [PATCH 36/60] Article: Fleet 4.48.0 release (#18062) Article: Fleet 4.48.0 release and capitalization fixes to CHANGELOG. #17531 --- CHANGELOG.md | 6 +- articles/fleet-4.48.0.md | 105 ++++++++++++++++++ .../articles/fleet-4.48.0-1600x900@2x.png | Bin 0 -> 52861 bytes 3 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 articles/fleet-4.48.0.md create mode 100644 website/assets/images/articles/fleet-4.48.0-1600x900@2x.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 85ece1a556..8d18839ae8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,7 @@ - Fixed a bug where valid MDM enrollments would show up as unmanaged (EnrollmentState 3). - Fixed flash message from closing when a modal closes. - Fixed a bug where OS version information would not get detected on Windows Server 2019. -- Fixed issue where getting host details failed when attempting to read the host's bitlocker status from the datastore. +- Fixed issue where getting host details failed when attempting to read the host's BitLocker status from the datastore. - Fixed false negative vulnerabilities on macOS Homebrew python packages. - Fixed styling of live query disabled warning. - Fixed issue where Windows MDM profile processing was skipping `` commands. @@ -36,11 +36,11 @@ - Fixed `GET fleet/os_versions` and `GET fleet/os_versions/[id]` so team users no longer have access to os versions on hosts from other teams. - `fleetctl gitops` now batch processes queries and policies. - Fixed UI bug to render the query platform correctly for queries imported from the standard query library. -- Fixed issue where microsoft edge was not reporting vulnerabilities. +- Fixed issue where Microsoft Edge was not reporting vulnerabilities. - Fixed a bug where all Windows MDM enrollments were detected as automatic. - Fixed a bug where `null` or excluded `smtp_settings` caused a UI 500. - Fixed query reports so they reset when there is a change to the selected platform or selected minimum osquery version. -- Fixed live query sort of sql result sort for both string and numerical columns. +- Fixed live query sort of SQL result sort for both string and numerical columns. ## Fleet 4.47.3 (Mar 26, 2024) diff --git a/articles/fleet-4.48.0.md b/articles/fleet-4.48.0.md new file mode 100644 index 0000000000..4bfdcb5527 --- /dev/null +++ b/articles/fleet-4.48.0.md @@ -0,0 +1,105 @@ +# Fleet 4.48.0 | IdP local account creation, VS Code extensions. + +![Fleet 4.48.0](../website/assets/images/articles/fleet-4.48.0-1600x900@2x.png) + +Fleet 4.48.0 is live. Check out the full [changelog](https://github.com/fleetdm/fleet/releases/tag/fleet-v4.48.0) or continue reading to get the highlights. +For upgrade instructions, see our [upgrade guide](https://fleetdm.com/docs/deploying/upgrading-fleet) in the Fleet docs. + +## Highlights + +* IdP local account creation +* Software inventory includes VS Code extensions + + + +### IdP local account creation + +Local account creation with an Identity Provider (IdP) now prefills and locks local account creation with details sourced from the IdP. This feature streamlines the initial setup experience during the macOS out-of-the-box setup process by ensuring that the full name and account name fields are automatically populated with the user's IdP username. Additionally, it enforces password policies to be applied before the account creation is finalized, adding an extra layer of security and compliance to the enrollment process. This update simplifies the onboarding experience for end-users, allowing them to log into their Mac with their IdP credentials seamlessly. It reflects Fleet's commitment to enhancing device management efficiency and security. Fleet empowers IT administrators to ensure a consistent and secure user experience across their macOS fleet by focusing on automating and securing the setup process. + + +### VS Code extensions + +In addressing the need for comprehensive software inventory management, Fleet has expanded its inventory capabilities to include Visual Studio Code (VS Code) extensions. This addition enables IT and security teams to gain visibility into the VS Code extensions installed across their device fleet, offering a clearer view of their environments' development tools and resources. By surfacing VS Code extensions in the software inventory, Fleet allows for a more detailed assessment of the software landscape, facilitating better compliance, security assessments, and software management practices. This feature aligns with Fleet's commitment to providing detailed and actionable insights into the software ecosystem, supporting informed decision-making and proactive management of digital assets. + + + + +## Changes + + +### Endpoint operations +- Added integration with Google Calendar. + * Fleet admins can enable Google Calendar integration by using a Google service account with domain-wide delegation. + * Calendar integration is enabled at the team level for specific team policies. + * If the policy is failing, a calendar event will be put on the host user's calendar for the 3rd Tuesday of the month. + * During the event, Fleet will fire a webhook. IT admins should use this webhook to trigger a script or MDM command that will remediate the issue. +- Reduced the number of 'Deadlock found' errors seen by the server when multiple hosts share the same UUID. +- Removed outdated tooltips from UI. +- Added hover states to clickable elements. +- Added cross-platform check for duplicate MDM profile names in batch set MDM profiles API. + +### Device management (MDM) +- Added Windows MDM support to the `osquery-perf` host-simulation command. +- Added a missing database index to the MDM Windows enrollments table that will improve performance at scale. +- Migrate MDM-related endpoints to new paths, deprecating (but still supporting indefinitely) the old endpoints. +- Adds API functionality for creating DDM declarations, both individually and as a batch. +- Added DDM activities to the fleet UI. +- Added the `enable_release_device_manually` configuration setting for a team and no team. **Note** that the macOS automatic enrollment profile cannot set the `await_device_configured` option anymore, this setting is controlled by Fleet via the new `enable_release_device_manually` option. +- Automatically release a macOS DEP-enrolled device after enrollment commands and profiles have been delivered, unless `enable_release_device_manually` is set to `true`. + +### Vulnerability management +- Added Visual Studio extensions to Fleet's software inventory. + +### Bug fixes +- Fixed a bug where valid MDM enrollments would show up as unmanaged (EnrollmentState 3). +- Fixed flash message from closing when a modal closes. +- Fixed a bug where OS version information would not get detected on Windows Server 2019. +- Fixed issue where getting host details failed when attempting to read the host's BitLocker status from the datastore. +- Fixed false negative vulnerabilities on macOS Homebrew python packages. +- Fixed styling of live query disabled warning. +- Fixed issue where Windows MDM profile processing was skipping `` commands. +- Fixed UI's ability to bulk delete hosts when "All teams" is selected. +- Fixed error state rendering on the global Host status expiry settings page, fix error state alignment for tooltip-wrapper field labels across organization settings. +- Fixed `GET fleet/os_versions` and `GET fleet/os_versions/[id]` so team users no longer have access to os versions on hosts from other teams. +- `fleetctl gitops` now batch processes queries and policies. +- Fixed UI bug to render the query platform correctly for queries imported from the standard query library. +- Fixed issue where Microsoft Edge was not reporting vulnerabilities. +- Fixed a bug where all Windows MDM enrollments were detected as automatic. +- Fixed a bug where `null` or excluded `smtp_settings` caused a UI 500. +- Fixed query reports so they reset when there is a change to the selected platform or selected minimum osquery version. +- Fixed live query sort of SQL result sort for both string and numerical columns. + +## Fleet 4.47.3 (Mar 26, 2024) + +### Bug fixes + +* Fixed a bug where valid Windows MDM enrollments would show up as unmanaged (EnrollmentState 3). + +## Fleet 4.47.2 (Mar 22, 2024) + +### Bug fixes + +* Fixed false negative vulnerabilities on macOS Homebrew Python packages. +* Fixed policies to check "disable guest user". +* Resolved the issue where Microsoft Edge was not reporting vulnerabilities. + +## Fleet 4.47.1 (Mar 18, 2024) + +### Bug fixes + +* Removed outdated tooltips from UI. +* Fixed an issue with Windows MDM profile processing where `` commands were being skipped. +* Team users no longer have access to OS versions on hosts from other teams for GET fleet/os_versions and GET fleet/os_versions/[id]. +* Reduced the number of 'Deadlock found' errors seen by the server when multiple hosts share the same UUID. + + +## Ready to upgrade? + +Visit our [Upgrade guide](https://fleetdm.com/docs/deploying/upgrading-fleet) in the Fleet docs for instructions on updating to Fleet 4.48.0. + + + + + + + diff --git a/website/assets/images/articles/fleet-4.48.0-1600x900@2x.png b/website/assets/images/articles/fleet-4.48.0-1600x900@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..e7e08af2deb83f51576ea65d39d46fcc71fe521e GIT binary patch literal 52861 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 Date: Thu, 4 Apr 2024 16:18:41 -0500 Subject: [PATCH 37/60] Fix panic due to nil calendar config. (#18065) #18063 Fixing potential server panic when events are created with calendar integration, but then global calendar integration is disabled. Could not do a PR directly from [fleet-v4.48.0](https://github.com/fleetdm/fleet/releases/tag/fleet-v4.48.0) due to merge conflicts. # Checklist for submitter - [x] Changes file added for user-visible changes in `changes/` or `orbit/changes/`. See [Changes files](https://fleetdm.com/docs/contributing/committing-changes#changes-files) for more information. - [ ] Added/updated tests - [x] Manual QA for all new/changed functionality --- changes/18065-calendar-config-panic | 1 + server/cron/calendar_cron.go | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 changes/18065-calendar-config-panic diff --git a/changes/18065-calendar-config-panic b/changes/18065-calendar-config-panic new file mode 100644 index 0000000000..4a4a82176b --- /dev/null +++ b/changes/18065-calendar-config-panic @@ -0,0 +1 @@ +Fixing potential server panic when events are created with calendar integration, but then global calendar integration is disabled. diff --git a/server/cron/calendar_cron.go b/server/cron/calendar_cron.go index cad8ba8513..3b9cd5b24f 100644 --- a/server/cron/calendar_cron.go +++ b/server/cron/calendar_cron.go @@ -671,7 +671,10 @@ func deleteCalendarEventsInParallel( go func() { defer wg.Done() for calEvent := range calendarEventCh { - userCalendar := createUserCalendarFromConfig(ctx, calendarConfig, logger) + var userCalendar fleet.UserCalendar + if calendarConfig != nil { + userCalendar = createUserCalendarFromConfig(ctx, calendarConfig, logger) + } if err := deleteCalendarEvent(ctx, ds, userCalendar, calEvent); err != nil { level.Error(logger).Log("msg", "delete user calendar event", "err", err) continue From 4671eb22d3d7ce4cc39f4ebbb98cc2a750b3ace2 Mon Sep 17 00:00:00 2001 From: Dave Herder <27025660+dherder@users.noreply.github.com> Date: Thu, 4 Apr 2024 14:28:42 -0700 Subject: [PATCH 38/60] Update communications.md (#18040) added link to trust portal under vendor questionnaires section --------- Co-authored-by: Sam Pfluger <108141731+Sampfluger88@users.noreply.github.com> --- handbook/company/communications.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handbook/company/communications.md b/handbook/company/communications.md index 007daa2b43..72df3299c7 100644 --- a/handbook/company/communications.md +++ b/handbook/company/communications.md @@ -493,7 +493,7 @@ You can learn more about how Fleet approaches security in the [security handbook ## Vendor questionnaires -In responding to security questionnaires, Fleet endeavors to provide full transparency via our [security policies](https://fleetdm.com/handbook/security/security-policies#security-policies) and [application security](https://fleetdm.com/handbook/business-operations/application-security) documentation. In addition to this documentation, please refer to [the vendor questionnaires page](https://fleetdm.com/handbook/business-operations/vendor-questionnaires) +In responding to security questionnaires, Fleet endeavors to provide full transparency via our [security policies](https://fleetdm.com/handbook/security/security-policies#security-policies), [trust](https://trust.fleetdm.com/), and [application security](https://fleetdm.com/handbook/business-operations/application-security) documentation. In addition to this documentation, please refer to [the vendor questionnaires page](https://fleetdm.com/handbook/business-operations/vendor-questionnaires). [Contact the Sales department](https://fleetdm.com/handbook/sales#contact-us) to address any pending questionnaires. ## Getting a contract signed If a contract is ready for signature and requires no review or revision, the requestor logins into DocuSign using hello@ from the 1Password vault and routes the agreement to the CEO for signature. From dda58e3dbafa76e44dbc26c2884729caa2715da8 Mon Sep 17 00:00:00 2001 From: Dave Herder <27025660+dherder@users.noreply.github.com> Date: Thu, 4 Apr 2024 14:28:49 -0700 Subject: [PATCH 39/60] Update README.md (#18011) added 'Process a security questionnaire' responsibility --------- Co-authored-by: Sam Pfluger <108141731+Sampfluger88@users.noreply.github.com> --- handbook/sales/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/handbook/sales/README.md b/handbook/sales/README.md index cd971a7b80..b1fe017ad4 100644 --- a/handbook/sales/README.md +++ b/handbook/sales/README.md @@ -143,6 +143,12 @@ To close a deal with a new customer (non-self-service), create and complete a Gi ### Change customer credit card number You can help a Premium license dispenser customers change their credit card by directing them to their [account dashboard](https://fleetdm.com/customers/dashboard). On that page, the customer can update their billing card by clicking the pencil icon next to their billing information. +### Process a security questionnaire +- The AE will [use the handbook](https://fleetdm.com/handbook/company/communications#vendor-questionnaires) to answer most of the questions with links to appropriate sections in the handbook. After this first pass has been completed, and if there are outstanding questions, the AE will [assign the issue to Business Operations (#g-business-operations)](https://fleetdm.com/handbook/business-operations#contact-us) with a requested timeline for completion defined. +- BizOps consults the handbook to validate that nothing was missed by the AE. After the second pass has been completed, and if there are outstanding questions, BizOps will [reassign the issue to Sales (#g-sales)](https://fleetdm.com/handbook/sales#contact-us) for intake. +- The issue will be assigned to the Solutions Consultant (SC) associated to the opportunity in order to complete any unanswered questions. +- The SC will search for unanswered questions and confirm again that nothing was missed from the handbook. Content missing from the handbook will need to be added via PR by the SC. Any unanswered questions after this pass has been completed by the SC will need to be [escalated to the Infrastructure team (#g-customer-success)](https://fleetdm.com/handbook/customer-success#contact-us) with the requested timeline for completion defined in the issue. Once complete, the infra team will assign the issue back to the #g-sales board. +- Any questions answered by the infra team will be added to the handbook by the SC. ## Rituals From 505084f790ad4db391557216f1361c6ba03f836d Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 4 Apr 2024 18:40:11 -0500 Subject: [PATCH 40/60] Website: add "EDR health checks" to /endpoint-ops (#18059) Closes: https://github.com/fleetdm/confidential/issues/5722 changes: - updated the /endpoint-ops page to match the latest wireframes. --- website/views/pages/endpoint-ops.ejs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/website/views/pages/endpoint-ops.ejs b/website/views/pages/endpoint-ops.ejs index 90eadceb08..6afe981f52 100644 --- a/website/views/pages/endpoint-ops.ejs +++ b/website/views/pages/endpoint-ops.ejs @@ -76,7 +76,7 @@ -
+
CIS benchmarks @@ -96,6 +96,15 @@

Track progress towards deadlines for security posture remediation projects, and enforce due dates through automations.

+
+
+ Verify updates and settings +
EDR health checks
+

Verify that your EDR tools are installed and working so you can identify and address configuration issues quickly.

+
+
+
+

*Currently limited to: macOS, Linux, Windows, Chromebooks, OT, data centers, Amazon Web Services (AWS), Google Cloud (GCP), and the Microsoft Cloud (Azure).

From bb932c4659854e4bcaad8721d83e832231b66e86 Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 4 Apr 2024 19:18:38 -0500 Subject: [PATCH 41/60] Website: remove /imagine pages (#18073) Changes: - Removed /imagine pages --- .../imagine/deliver-launch-party-signup.js | 78 ---- .../api/controllers/imagine/view-defcon-31.js | 33 -- .../imagine/view-higher-education.js | 33 -- .../imagine/view-jamf-alternative.js | 29 -- .../controllers/imagine/view-launch-party.js | 65 ---- .../imagine/view-rapid-7-alternative.js | 34 -- .../imagine/view-unused-software.js | 33 -- .../assets/js/pages/imagine/defcon-31.page.js | 25 -- .../js/pages/imagine/higher-education.page.js | 25 -- .../js/pages/imagine/jamf-alternative.page.js | 25 -- .../js/pages/imagine/launch-party.page.js | 74 ---- .../pages/imagine/rapid-7-alternative.page.js | 25 -- .../js/pages/imagine/unused-software.page.js | 25 -- website/assets/styles/importer.less | 8 - .../styles/pages/imagine/defcon-31.less | 356 ----------------- .../pages/imagine/higher-education.less | 347 ----------------- .../pages/imagine/jamf-alternative.less | 347 ----------------- .../styles/pages/imagine/launch-party.less | 192 --------- .../pages/imagine/rapid-7-alternative.less | 347 ----------------- .../styles/pages/imagine/unused-software.less | 367 ------------------ website/config/routes.js | 34 -- website/views/pages/imagine/defcon-31.ejs | 71 ---- .../views/pages/imagine/higher-education.ejs | 100 ----- .../views/pages/imagine/jamf-alternative.ejs | 74 ---- website/views/pages/imagine/launch-party.ejs | 109 ------ .../pages/imagine/rapid-7-alternative.ejs | 115 ------ .../views/pages/imagine/unused-software.ejs | 98 ----- 27 files changed, 3069 deletions(-) delete mode 100644 website/api/controllers/imagine/deliver-launch-party-signup.js delete mode 100644 website/api/controllers/imagine/view-defcon-31.js delete mode 100644 website/api/controllers/imagine/view-higher-education.js delete mode 100644 website/api/controllers/imagine/view-jamf-alternative.js delete mode 100644 website/api/controllers/imagine/view-launch-party.js delete mode 100644 website/api/controllers/imagine/view-rapid-7-alternative.js delete mode 100644 website/api/controllers/imagine/view-unused-software.js delete mode 100644 website/assets/js/pages/imagine/defcon-31.page.js delete mode 100644 website/assets/js/pages/imagine/higher-education.page.js delete mode 100644 website/assets/js/pages/imagine/jamf-alternative.page.js delete mode 100644 website/assets/js/pages/imagine/launch-party.page.js delete mode 100644 website/assets/js/pages/imagine/rapid-7-alternative.page.js delete mode 100644 website/assets/js/pages/imagine/unused-software.page.js delete mode 100644 website/assets/styles/pages/imagine/defcon-31.less delete mode 100644 website/assets/styles/pages/imagine/higher-education.less delete mode 100644 website/assets/styles/pages/imagine/jamf-alternative.less delete mode 100644 website/assets/styles/pages/imagine/launch-party.less delete mode 100644 website/assets/styles/pages/imagine/rapid-7-alternative.less delete mode 100644 website/assets/styles/pages/imagine/unused-software.less delete mode 100644 website/views/pages/imagine/defcon-31.ejs delete mode 100644 website/views/pages/imagine/higher-education.ejs delete mode 100644 website/views/pages/imagine/jamf-alternative.ejs delete mode 100644 website/views/pages/imagine/launch-party.ejs delete mode 100644 website/views/pages/imagine/rapid-7-alternative.ejs delete mode 100644 website/views/pages/imagine/unused-software.ejs diff --git a/website/api/controllers/imagine/deliver-launch-party-signup.js b/website/api/controllers/imagine/deliver-launch-party-signup.js deleted file mode 100644 index 3dffd39599..0000000000 --- a/website/api/controllers/imagine/deliver-launch-party-signup.js +++ /dev/null @@ -1,78 +0,0 @@ -module.exports = { - - - friendlyName: 'Deliver launch party signup', - - - description: 'Delivers a form submission to a Zapier webhook when someone RSVPs to our MDM launch party.', - - - inputs: { - - emailAddress: { - required: true, - type: 'string', - description: 'The email address provided when a user submitted the launch party waitlist form.', - example: 'hermione@hogwarts.edu' - }, - - firstName: { - required: true, - type: 'string', - description: 'The first name provided when a user submitted the launch party waitlist form', - }, - - lastName: { - required: true, - type: 'string', - description: 'The last name provided when a user submitted the launch party waitlist form', - }, - - jobTitle: { - type: 'string', - description: 'The job title provided when a user submitted the launch party waitlist form', - }, - - phoneNumber: { - type: 'string', - description: 'The phone number provided when a user submitted the launch party waitlist form', - }, - }, - - - exits: { - - success: { - description: 'The message was sent successfully.' - } - - }, - - - fn: async function({emailAddress, firstName, lastName, jobTitle, phoneNumber}) { - - if(!sails.config.custom.zapierSandboxWebhookSecret) { - throw new Error('Message not delivered: zapierSandboxWebhookSecret needs to be configured in sails.config.custom.'); - } - // Send a POST request to Zapier - await sails.helpers.http.post( - 'https://hooks.zapier.com/hooks/catch/3627242/33kdpw0/', - { - 'firstName': firstName, - 'lastName': lastName, - 'emailAddress': emailAddress, - 'jobTitle': jobTitle, - 'phoneNumber': phoneNumber, - 'webhookSecret': sails.config.custom.zapierSandboxWebhookSecret - } - ) - .timeout(5000) - .tolerate(['non200Response', 'requestFailed', {name: 'TimeoutError'}], (err)=>{ - // Note that Zapier responds with a 2xx status code even if something goes wrong, so just because this message is not logged doesn't mean everything is hunky dory. More info: https://github.com/fleetdm/fleet/pull/6380#issuecomment-1204395762 - sails.log.warn(`When a user submitted the launch party waitlist form, an error occurred while sending a request to Zapier. Raw error: ${require('util').inspect(err)}`); - return; - }); - - } - -}; diff --git a/website/api/controllers/imagine/view-defcon-31.js b/website/api/controllers/imagine/view-defcon-31.js deleted file mode 100644 index b1d5a93d0c..0000000000 --- a/website/api/controllers/imagine/view-defcon-31.js +++ /dev/null @@ -1,33 +0,0 @@ -module.exports = { - - - friendlyName: 'View defcon 31', - - - description: 'Display "Defcon 31" page.', - - - exits: { - - success: { - viewTemplatePath: 'pages/imagine/defcon-31' - }, - badConfig: { responseType: 'badConfig' }, - }, - - - fn: async function () { - if (!_.isObject(sails.config.builtStaticContent) || !_.isArray(sails.config.builtStaticContent.testimonials) || !sails.config.builtStaticContent.compiledPagePartialsAppPath) { - throw {badConfig: 'builtStaticContent.testimonials'}; - } - // Get testimonials for the component. - let testimonialsForScrollableTweets = sails.config.builtStaticContent.testimonials; - // Respond with view. - return { - testimonialsForScrollableTweets, - }; - - } - - -}; diff --git a/website/api/controllers/imagine/view-higher-education.js b/website/api/controllers/imagine/view-higher-education.js deleted file mode 100644 index 1cdcbf9259..0000000000 --- a/website/api/controllers/imagine/view-higher-education.js +++ /dev/null @@ -1,33 +0,0 @@ -module.exports = { - - - friendlyName: 'View higher education', - - - description: 'Display "Higher education" page.', - - - exits: { - - success: { - viewTemplatePath: 'pages/imagine/higher-education' - }, - badConfig: { responseType: 'badConfig' }, - }, - - - fn: async function () { - if (!_.isObject(sails.config.builtStaticContent) || !_.isArray(sails.config.builtStaticContent.testimonials) || !sails.config.builtStaticContent.compiledPagePartialsAppPath) { - throw {badConfig: 'builtStaticContent.testimonials'}; - } - // Get testimonials for the component. - let testimonialsForScrollableTweets = sails.config.builtStaticContent.testimonials; - // Respond with view. - return { - testimonialsForScrollableTweets, - }; - - } - - -}; diff --git a/website/api/controllers/imagine/view-jamf-alternative.js b/website/api/controllers/imagine/view-jamf-alternative.js deleted file mode 100644 index fdfed3b28f..0000000000 --- a/website/api/controllers/imagine/view-jamf-alternative.js +++ /dev/null @@ -1,29 +0,0 @@ -module.exports = { - - - friendlyName: 'View jamf alternative', - - - description: 'Display "Jamf alternative" page.', - - - exits: { - - success: { - viewTemplatePath: 'pages/imagine/jamf-alternative' - } - - }, - - - fn: async function () { - // Get testimonials for the component. - let testimonialsForScrollableTweets = sails.config.builtStaticContent.testimonials; - // Respond with view. - return { - testimonialsForScrollableTweets, - }; - } - - -}; diff --git a/website/api/controllers/imagine/view-launch-party.js b/website/api/controllers/imagine/view-launch-party.js deleted file mode 100644 index 268fdbcbc7..0000000000 --- a/website/api/controllers/imagine/view-launch-party.js +++ /dev/null @@ -1,65 +0,0 @@ -module.exports = { - - - friendlyName: 'View launch party', - - - description: 'Display "Launch party" page.', - - - inputs: { - showForm: { - type: 'boolean', - description: 'An optional boolean that if provided with other', - defaultsTo: false - }, - emailAddress: { - type: 'string', - description: 'If provided, this value will be used to prefill the emailAddress field in the waitlist form' - }, - firstName: { - type: 'string', - description: 'If provided, this value will be used to prefill the first name field in the waitlist form' - }, - lastName: { - type: 'string', - description: 'If provided, this value will be used to prefill the last name field in the waitlist form' - } - }, - - - exits: { - - success: { - viewTemplatePath: 'pages/imagine/launch-party' - } - - }, - - - fn: async function ({showForm, emailAddress, firstName, lastName}) { - - // If form inputs are provided via query string we'll prefill the inputs in the waitlist form. (e.g., A user is coming to this page from a personalized link in an email) - let formDataToPrefill = {}; - if(emailAddress){// Email address will always be provided if a user is coming here from an email link. - formDataToPrefill.emailAddress = emailAddress; - } - // If the first name provided is not '?' or Outreach's first name template, we'll prefill the first name in the waitlist form. - if(firstName && firstName !== '?' && firstName !== '{{first_name}}') { - formDataToPrefill.firstName = firstName; - } - // If the last name provided is not '?' or Outreach's last name template, we'll prefill the last name in the waitlist form. - if(lastName && lastName !== '?' && lastName !== '{{last_name}}') { - formDataToPrefill.lastName = lastName; - } - - // Respond with view. - return { - showForm, - formDataToPrefill, - }; - - } - - -}; diff --git a/website/api/controllers/imagine/view-rapid-7-alternative.js b/website/api/controllers/imagine/view-rapid-7-alternative.js deleted file mode 100644 index 7aaaf6b0ba..0000000000 --- a/website/api/controllers/imagine/view-rapid-7-alternative.js +++ /dev/null @@ -1,34 +0,0 @@ -module.exports = { - - - friendlyName: 'View rapid 7 alternative', - - - description: 'Display "Rapid 7 alternative" page.', - - - exits: { - - success: { - viewTemplatePath: 'pages/imagine/rapid-7-alternative' - }, - - badConfig: { responseType: 'badConfig' }, - }, - - - fn: async function () { - if (!_.isObject(sails.config.builtStaticContent) || !_.isArray(sails.config.builtStaticContent.testimonials) || !sails.config.builtStaticContent.compiledPagePartialsAppPath) { - throw {badConfig: 'builtStaticContent.testimonials'}; - } - // Get testimonials for the component. - let testimonialsForScrollableTweets = sails.config.builtStaticContent.testimonials; - // Respond with view. - return { - testimonialsForScrollableTweets, - }; - - } - - -}; diff --git a/website/api/controllers/imagine/view-unused-software.js b/website/api/controllers/imagine/view-unused-software.js deleted file mode 100644 index ed8f28cca5..0000000000 --- a/website/api/controllers/imagine/view-unused-software.js +++ /dev/null @@ -1,33 +0,0 @@ -module.exports = { - - - friendlyName: 'View unused software', - - - description: 'Display "Unused software" page.', - - - exits: { - - success: { - viewTemplatePath: 'pages/imagine/unused-software' - }, - badConfig: { responseType: 'badConfig' }, - }, - - - fn: async function () { - if (!_.isObject(sails.config.builtStaticContent) || !_.isArray(sails.config.builtStaticContent.testimonials) || !sails.config.builtStaticContent.compiledPagePartialsAppPath) { - throw {badConfig: 'builtStaticContent.testimonials'}; - } - // Get testimonials for the component. - let testimonialsForScrollableTweets = sails.config.builtStaticContent.testimonials; - // Respond with view. - return { - testimonialsForScrollableTweets, - }; - - } - - -}; diff --git a/website/assets/js/pages/imagine/defcon-31.page.js b/website/assets/js/pages/imagine/defcon-31.page.js deleted file mode 100644 index 888d819b37..0000000000 --- a/website/assets/js/pages/imagine/defcon-31.page.js +++ /dev/null @@ -1,25 +0,0 @@ -parasails.registerPage('defcon-31', { - // ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗ - // β•‘β•‘β•‘β•‘β•‘ β•‘ ║╠═╣║ β•šβ•β•— β•‘ ╠═╣ β•‘ β•‘β•£ - // β•©β•β•šβ•β•© β•© β•©β•© ╩╩═╝ β•šβ•β• β•© β•© β•© β•© β•šβ•β• - data: { - //… - }, - - // ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗ - // β•‘ β•‘β• β•£ β•‘β•£ β•‘ β•šβ•¦β•β•‘ β•‘ β•‘β•£ - // β•©β•β•β•©β•š β•šβ•β•β•šβ•β• β•© β•šβ•β•β•©β•β•β•šβ•β• - beforeMount: function() { - //… - }, - mounted: async function() { - //… - }, - - // ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗ - // β•‘β•‘β•‘β•‘ β•‘ β•‘β•£ ╠╦╝╠═╣║ β•‘ β•‘β•‘ β•‘β•‘β•‘β•‘β•šβ•β•— - // β•©β•β•šβ• β•© β•šβ•β•β•©β•šβ•β•© β•©β•šβ•β• β•© β•©β•šβ•β•β•β•šβ•β•šβ•β• - methods: { - //… - } -}); diff --git a/website/assets/js/pages/imagine/higher-education.page.js b/website/assets/js/pages/imagine/higher-education.page.js deleted file mode 100644 index 8dc1330187..0000000000 --- a/website/assets/js/pages/imagine/higher-education.page.js +++ /dev/null @@ -1,25 +0,0 @@ -parasails.registerPage('higher-education', { - // ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗ - // β•‘β•‘β•‘β•‘β•‘ β•‘ ║╠═╣║ β•šβ•β•— β•‘ ╠═╣ β•‘ β•‘β•£ - // β•©β•β•šβ•β•© β•© β•©β•© ╩╩═╝ β•šβ•β• β•© β•© β•© β•© β•šβ•β• - data: { - //… - }, - - // ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗ - // β•‘ β•‘β• β•£ β•‘β•£ β•‘ β•šβ•¦β•β•‘ β•‘ β•‘β•£ - // β•©β•β•β•©β•š β•šβ•β•β•šβ•β• β•© β•šβ•β•β•©β•β•β•šβ•β• - beforeMount: function() { - //… - }, - mounted: async function() { - //… - }, - - // ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗ - // β•‘β•‘β•‘β•‘ β•‘ β•‘β•£ ╠╦╝╠═╣║ β•‘ β•‘β•‘ β•‘β•‘β•‘β•‘β•šβ•β•— - // β•©β•β•šβ• β•© β•šβ•β•β•©β•šβ•β•© β•©β•šβ•β• β•© β•©β•šβ•β•β•β•šβ•β•šβ•β• - methods: { - //… - } -}); diff --git a/website/assets/js/pages/imagine/jamf-alternative.page.js b/website/assets/js/pages/imagine/jamf-alternative.page.js deleted file mode 100644 index d795588682..0000000000 --- a/website/assets/js/pages/imagine/jamf-alternative.page.js +++ /dev/null @@ -1,25 +0,0 @@ -parasails.registerPage('jamf-alternative', { - // ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗ - // β•‘β•‘β•‘β•‘β•‘ β•‘ ║╠═╣║ β•šβ•β•— β•‘ ╠═╣ β•‘ β•‘β•£ - // β•©β•β•šβ•β•© β•© β•©β•© ╩╩═╝ β•šβ•β• β•© β•© β•© β•© β•šβ•β• - data: { - //… - }, - - // ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗ - // β•‘ β•‘β• β•£ β•‘β•£ β•‘ β•šβ•¦β•β•‘ β•‘ β•‘β•£ - // β•©β•β•β•©β•š β•šβ•β•β•šβ•β• β•© β•šβ•β•β•©β•β•β•šβ•β• - beforeMount: function() { - //… - }, - mounted: async function() { - //… - }, - - // ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗ - // β•‘β•‘β•‘β•‘ β•‘ β•‘β•£ ╠╦╝╠═╣║ β•‘ β•‘β•‘ β•‘β•‘β•‘β•‘β•šβ•β•— - // β•©β•β•šβ• β•© β•šβ•β•β•©β•šβ•β•© β•©β•šβ•β• β•© β•©β•šβ•β•β•β•šβ•β•šβ•β• - methods: { - //… - } -}); diff --git a/website/assets/js/pages/imagine/launch-party.page.js b/website/assets/js/pages/imagine/launch-party.page.js deleted file mode 100644 index bd2557d15b..0000000000 --- a/website/assets/js/pages/imagine/launch-party.page.js +++ /dev/null @@ -1,74 +0,0 @@ -parasails.registerPage('launch-party', { - // ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗ - // β•‘β•‘β•‘β•‘β•‘ β•‘ ║╠═╣║ β•šβ•β•— β•‘ ╠═╣ β•‘ β•‘β•£ - // β•©β•β•šβ•β•© β•© β•©β•© ╩╩═╝ β•šβ•β• β•© β•© β•© β•© β•šβ•β• - data: { - formData: { /* … */ }, - - // For tracking client-side validation errors in our form. - // > Has property set to `true` for each invalid property in `formData`. - formErrors: { /* … */ }, - - // Form rules - formRules: { - firstName: {required: true }, - lastName: {required: true }, - emailAddress: {required: true, isEmail: true}, - }, - cloudError: '', - // Syncing / loading state - syncing: false, - showSignupFormSuccess: false, - // Modal - - modal: '', - showAlternateWaitlistText: false, - }, - - // ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗ - // β•‘ β•‘β• β•£ β•‘β•£ β•‘ β•šβ•¦β•β•‘ β•‘ β•‘β•£ - // β•©β•β•β•©β•š β•šβ•β•β•šβ•β• β•© β•šβ•β•β•©β•β•β•šβ•β• - beforeMount: function() { - - //… - }, - mounted: async function() { - - if(this.showForm) { - this.modal = 'happy-hour-waitlist'; - if(!_.isEmpty(this.formDataToPrefill)){ - // If the user came here via a personalized link in an email, we'll prefill the form with the user information (if provided) - this.formData = this.formDataToPrefill; - this.showAlternateWaitlistText = true; - } - } - - }, - - // ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗ - // β•‘β•‘β•‘β•‘ β•‘ β•‘β•£ ╠╦╝╠═╣║ β•‘ β•‘β•‘ β•‘β•‘β•‘β•‘β•šβ•β•— - // β•©β•β•šβ• β•© β•šβ•β•β•©β•šβ•β•© β•©β•šβ•β• β•© β•©β•šβ•β•β•β•šβ•β•šβ•β• - methods: { - clickOpenModal: function() { - this.modal = 'happy-hour-waitlist'; - }, - closeModal: async function () { - this.modal = ''; - await this._resetForms(); - }, - typeClearOneFormError: async function(field) { - if(this.formErrors[field]){ - this.formErrors = _.omit(this.formErrors, field); - } - }, - submittedForm: function() { - this.showSignupFormSuccess = true; - }, - _resetForms: async function() { - this.cloudError = ''; - this.formData = {}; - this.formErrors = {}; - await this.forceRender(); - }, - } -}); diff --git a/website/assets/js/pages/imagine/rapid-7-alternative.page.js b/website/assets/js/pages/imagine/rapid-7-alternative.page.js deleted file mode 100644 index ee5e1d3fa9..0000000000 --- a/website/assets/js/pages/imagine/rapid-7-alternative.page.js +++ /dev/null @@ -1,25 +0,0 @@ -parasails.registerPage('rapid-7-alternative', { - // ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗ - // β•‘β•‘β•‘β•‘β•‘ β•‘ ║╠═╣║ β•šβ•β•— β•‘ ╠═╣ β•‘ β•‘β•£ - // β•©β•β•šβ•β•© β•© β•©β•© ╩╩═╝ β•šβ•β• β•© β•© β•© β•© β•šβ•β• - data: { - //… - }, - - // ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗ - // β•‘ β•‘β• β•£ β•‘β•£ β•‘ β•šβ•¦β•β•‘ β•‘ β•‘β•£ - // β•©β•β•β•©β•š β•šβ•β•β•šβ•β• β•© β•šβ•β•β•©β•β•β•šβ•β• - beforeMount: function() { - //… - }, - mounted: async function() { - //… - }, - - // ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗ - // β•‘β•‘β•‘β•‘ β•‘ β•‘β•£ ╠╦╝╠═╣║ β•‘ β•‘β•‘ β•‘β•‘β•‘β•‘β•šβ•β•— - // β•©β•β•šβ• β•© β•šβ•β•β•©β•šβ•β•© β•©β•šβ•β• β•© β•©β•šβ•β•β•β•šβ•β•šβ•β• - methods: { - //… - } -}); diff --git a/website/assets/js/pages/imagine/unused-software.page.js b/website/assets/js/pages/imagine/unused-software.page.js deleted file mode 100644 index f51149586e..0000000000 --- a/website/assets/js/pages/imagine/unused-software.page.js +++ /dev/null @@ -1,25 +0,0 @@ -parasails.registerPage('unused-software', { - // ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗ - // β•‘β•‘β•‘β•‘β•‘ β•‘ ║╠═╣║ β•šβ•β•— β•‘ ╠═╣ β•‘ β•‘β•£ - // β•©β•β•šβ•β•© β•© β•©β•© ╩╩═╝ β•šβ•β• β•© β•© β•© β•© β•šβ•β• - data: { - //… - }, - - // ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗ - // β•‘ β•‘β• β•£ β•‘β•£ β•‘ β•šβ•¦β•β•‘ β•‘ β•‘β•£ - // β•©β•β•β•©β•š β•šβ•β•β•šβ•β• β•© β•šβ•β•β•©β•β•β•šβ•β• - beforeMount: function() { - //… - }, - mounted: async function() { - //… - }, - - // ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗ - // β•‘β•‘β•‘β•‘ β•‘ β•‘β•£ ╠╦╝╠═╣║ β•‘ β•‘β•‘ β•‘β•‘β•‘β•‘β•šβ•β•— - // β•©β•β•šβ• β•© β•šβ•β•β•©β•šβ•β•© β•©β•šβ•β• β•© β•©β•šβ•β•β•β•šβ•β•šβ•β• - methods: { - //… - } -}); diff --git a/website/assets/styles/importer.less b/website/assets/styles/importer.less index e6b2c2829d..e6624efd3d 100644 --- a/website/assets/styles/importer.less +++ b/website/assets/styles/importer.less @@ -78,11 +78,3 @@ @import 'pages/try-fleet/explore-data.less'; @import 'pages/start.less'; - -// Imagine = landing pages for Marketing -@import 'pages/imagine/launch-party.less'; -@import 'pages/imagine/unused-software.less'; -@import 'pages/imagine/higher-education.less'; -@import 'pages/imagine/rapid-7-alternative.less'; -@import 'pages/imagine/defcon-31.less'; -@import 'pages/imagine/jamf-alternative.less'; diff --git a/website/assets/styles/pages/imagine/defcon-31.less b/website/assets/styles/pages/imagine/defcon-31.less deleted file mode 100644 index 1886c22b4c..0000000000 --- a/website/assets/styles/pages/imagine/defcon-31.less +++ /dev/null @@ -1,356 +0,0 @@ -#defcon-31 { - - h1 { - font-weight: 800; - font-size: 64px; - line-height: 76px; - } - h2 { - font-weight: 800; - font-size: 32px; - line-height: 38px; - margin-bottom: 40px; - } - h3 { - font-weight: 800; - font-size: 24px; - line-height: 32px; - } - h4 { - font-family: 'Roboto Mono'; - font-style: normal; - font-weight: 400; - font-size: 18px; - line-height: 24px; - color: @core-fleet-black-75; - margin-bottom: 4px; - } - p { - font-size: 16px; - line-height: 24px; - } - [purpose='hero'] { - background-image: url('/images/defcon-hero.png'); - height: 530px; - background-size: cover; - background-repeat: no-repeat; - background-position: center; - width: 100%; - margin-bottom: 40px; - border-radius: 8px; - } - [purpose='hero-background'] { - background: linear-gradient(180deg, #E8F1F7 3.37%, #FFFFFF 60%); - padding-top: 120px; - padding-bottom: 40px; - } - [purpose='hero-container'] { - max-width: 1200px; - } - [purpose='hero-text'] { - text-align: center; - max-width: 940px; - padding: 0 40px; - h1 { - margin-bottom: 32px; - } - p { - font-size: 18px; - line-height: 27px; - max-width: 640px; - margin: 0 auto 32px; - } - } - - [purpose='button-row'] { - a { - font-weight: 700; - font-size: 16px; - line-height: 24px; - } - [purpose='cta-button'] { - cursor: pointer; - margin-right: 32px; - background: @core-vibrant-red; - border-radius: 8px; - padding-left: 32px; - padding-right: 32px; - height: 48px; - display: flex; - justify-content: center; - align-items: center; - color: #FFF; - position: relative; - text-decoration: none; - } - [purpose='cta-button']::before { - background: linear-gradient(180deg, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0) 100%); - opacity: 1; - content: ' '; - position: absolute; - top: 0; - left: -5px; - width: 50%; - height: 100%; - transform: skew(-10deg); - transition: left 0.5s ease-in, opacity 0.50s ease-in, width 0.5s ease-in; - } - [purpose='cta-button']:hover:before { - opacity: 0; - left: 160px; - width: 110%; - } - - [purpose='animated-arrow-button-red'] { - display: inline; - padding-right: 40px; - cursor: pointer; - position: relative; - width: fit-content; - min-width: 125px; - font-weight: bold; - user-select: none; - transition: 0.2s ease-in-out; - -o-transition: 0.2s ease-in-out; - -ms-transition: 0.2s ease-in-out; - -moz-transition: 0.2s ease-in-out; - -webkit-transition: 0.2s ease-in-out; - color: @core-fleet-black; - text-decoration: none; - &:after { - content: url('/images/arrow-right-red-16x16@2x.png'); - transform: scale(0.5); - position: absolute; - top: -5px; - left: 80%; // <--- here - transition: 0.2s ease-in-out; - -o-transition: 0.2s ease-in-out; - -ms-transition: 0.2s ease-in-out; - -moz-transition: 0.2s ease-in-out; - -webkit-transition: 0.2s ease-in-out; - /* opacity: 0; */ - } - &:hover:after { - left: 82%; // <--- here - transition: 0.2s ease-in-out; - -o-transition: 0.2s ease-in-out; - -ms-transition: 0.2s ease-in-out; - -moz-transition: 0.2s ease-in-out; - -webkit-transition: 0.2s ease-in-out; - /* opacity:1; */ - } - } - } - - [purpose='page-container'] { - padding-left: 40px; - padding-right: 40px; - padding-bottom: 120px; - } - - [purpose='feature'] { - margin-top: 120px; - max-width: 960px; - h3 { - margin-bottom: 24px; - } - div { - max-width: 480px; - } - } - - [purpose='feature-image'] { - img { - max-height: 300px; - } - } - [purpose='large-feature-image'] { - img { - max-height: 250px; - } - } - - [purpose='checklists'] { - margin-top: 40px; - margin-bottom: 80px; - } - [purpose='checklist'] { - p { - padding-left: 28px; - text-indent: -28px; - } - p::before { - content: ' '; - background-image: url('/images/icon-checkmark-circle.svg'); - background-size: 16px 16px; - display: inline-block; - position: relative; - top: 2px; - margin-right: 12px; - width: 16px; - height: 16px; - } - } - - [purpose='tweets-container'] { - padding-top: 120px; - padding-bottom: 120px; - } - - [purpose='bottom-gradient'] { - background: linear-gradient(180deg, #FFFFFF 0%, #E9F4F4 100%); - } - [purpose='bottom-cloud-city-banner'] { - background: linear-gradient(180deg, #E9F4F4 0%, #FFFFFF 100%); - img { - width: 100%; - } - } - - @media (min-width: 1200px) { - [purpose='page-container'] { - padding-left: 120px; - padding-right: 120px; - } - } - - @media (max-width: 1200px) { - [purpose='logos'] { - height: 58px; - margin-left: auto; - margin-right: auto; - img { - margin-top: 23.5px; - margin-bottom: 23.5px; - padding-left: 25px; - padding-right: 25px; - } - [purpose='snowflake-logo'] { - height: 21px; - } - [purpose='wayfair-logo'] { - height: 23px; - } - [purpose='uber-logo'] { - height: 20px; - } - [purpose='atlassian-logo'] { - height: 16px; - } - [purpose='segment-logo'] { - height: 22px; - } - } - } - - @media (max-width: 991px) { - - [purpose='button-row'] { - margin-right: auto; - margin-left: auto; - } - - [purpose='hero-background'] { - padding-top: 120px; - padding-bottom: 80px; - padding-left: 0px; - padding-right: 0px; - } - - [purpose='logos'] { - height: 58px; - margin-left: 20px; - margin-right: 20px; - img { - margin-top: 17.5px; - margin-bottom: 17.5px; - padding-left: 10px; - padding-right: 10px; - } - } - } - - @media (max-width: 768px) { - - h1 { - font-size: 48px; - line-height: 54px; - } - - [purpose='page-container'] { - padding-left: 40px; - padding-right: 40px; - } - - [purpose='checklists'] { - margin-bottom: 40px; - } - - [purpose='feature'] { - margin-top: 80px; - } - - [purpose='feature-image'], [purpose='large-feature-image'] { - margin-bottom: 40px; - img { - max-height: 100%; - max-width: 100%; - } - } - - [purpose='hero-background'] { - padding-top: 60px; - padding-bottom: 60px; - padding-left: 20px; - padding-right: 20px; - } - - [purpose='hero-text'] { - text-align: center; - max-width: 480px; - } - - [purpose='tweets-container'] { - padding-top: 80px; - padding-bottom: 80px; - padding-left: 40px; - padding-right: 40px; - } - - [purpose='logos'] { - height: auto; - } - } - - @media (max-width: 575px) { - - [purpose='button-row'] { - max-width: 100%; - [purpose='cta-button'] { - margin-right: 0px; - width: 100%; - margin-bottom: 24px; - } - } - - [purpose='feature'] { - margin-top: 60px; - } - - [purpose='page-container'] { - padding-left: 20px; - padding-right: 20px; - } - - [purpose='tweets-container'] { - padding-left: 20px; - padding-right: 20px; - } - - [parasails-component='scrollable-tweets'] [purpose='tweets'] { - margin-top: 40px; - } - - } - -} diff --git a/website/assets/styles/pages/imagine/higher-education.less b/website/assets/styles/pages/imagine/higher-education.less deleted file mode 100644 index 10a3038cc4..0000000000 --- a/website/assets/styles/pages/imagine/higher-education.less +++ /dev/null @@ -1,347 +0,0 @@ -#higher-education { - - h1 { - font-weight: 800; - font-size: 64px; - line-height: 76px; - } - h2 { - font-weight: 800; - font-size: 32px; - line-height: 38px; - margin-bottom: 40px; - } - h3 { - font-weight: 800; - font-size: 24px; - line-height: 32px; - } - h4 { - font-family: 'Roboto Mono'; - font-style: normal; - font-weight: 400; - font-size: 18px; - line-height: 24px; - color: @core-fleet-black-75; - margin-bottom: 4px; - } - p { - font-size: 16px; - line-height: 24px; - } - - [purpose='hero-background'] { - background: linear-gradient(180deg, #E8F1F7 3.37%, #FFFFFF 60%); - padding-top: 120px; - padding-bottom: 40px; - } - [purpose='hero-container'] { - max-width: 1200px; - } - [purpose='hero-text'] { - text-align: center; - max-width: 940px; - padding: 0 40px; - h1 { - margin-bottom: 32px; - } - p { - font-size: 18px; - line-height: 27px; - max-width: 640px; - margin: 0 auto 32px; - } - } - - [purpose='button-row'] { - a { - font-weight: 700; - font-size: 16px; - line-height: 24px; - } - [purpose='cta-button'] { - cursor: pointer; - margin-right: 32px; - background: @core-vibrant-red; - border-radius: 8px; - padding-left: 32px; - padding-right: 32px; - height: 48px; - display: flex; - justify-content: center; - align-items: center; - color: #FFF; - position: relative; - text-decoration: none; - } - [purpose='cta-button']::before { - background: linear-gradient(180deg, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0) 100%); - opacity: 1; - content: ' '; - position: absolute; - top: 0; - left: -5px; - width: 50%; - height: 100%; - transform: skew(-10deg); - transition: left 0.5s ease-in, opacity 0.50s ease-in, width 0.5s ease-in; - } - [purpose='cta-button']:hover:before { - opacity: 0; - left: 160px; - width: 110%; - } - - [purpose='animated-arrow-button-red'] { - display: inline; - padding-right: 40px; - cursor: pointer; - position: relative; - width: fit-content; - min-width: 125px; - font-weight: bold; - user-select: none; - transition: 0.2s ease-in-out; - -o-transition: 0.2s ease-in-out; - -ms-transition: 0.2s ease-in-out; - -moz-transition: 0.2s ease-in-out; - -webkit-transition: 0.2s ease-in-out; - color: @core-fleet-black; - text-decoration: none; - &:after { - content: url('/images/arrow-right-red-16x16@2x.png'); - transform: scale(0.5); - position: absolute; - top: -5px; - left: 80%; // <--- here - transition: 0.2s ease-in-out; - -o-transition: 0.2s ease-in-out; - -ms-transition: 0.2s ease-in-out; - -moz-transition: 0.2s ease-in-out; - -webkit-transition: 0.2s ease-in-out; - /* opacity: 0; */ - } - &:hover:after { - left: 82%; // <--- here - transition: 0.2s ease-in-out; - -o-transition: 0.2s ease-in-out; - -ms-transition: 0.2s ease-in-out; - -moz-transition: 0.2s ease-in-out; - -webkit-transition: 0.2s ease-in-out; - /* opacity:1; */ - } - } - } - - [purpose='page-container'] { - padding-left: 40px; - padding-right: 40px; - padding-bottom: 120px; - } - - [purpose='feature'] { - margin-top: 120px; - max-width: 960px; - h3 { - margin-bottom: 24px; - } - div { - max-width: 480px; - } - } - - [purpose='feature-image'] { - img { - max-height: 300px; - } - } - [purpose='large-feature-image'] { - img { - max-height: 250px; - } - } - - [purpose='checklists'] { - margin-top: 40px; - margin-bottom: 80px; - } - [purpose='checklist'] { - p { - padding-left: 28px; - text-indent: -28px; - } - p::before { - content: ' '; - background-image: url('/images/icon-checkmark-circle.svg'); - background-size: 16px 16px; - display: inline-block; - position: relative; - top: 2px; - margin-right: 12px; - width: 16px; - height: 16px; - } - } - - [purpose='tweets-container'] { - padding-top: 120px; - padding-bottom: 120px; - } - - [purpose='bottom-gradient'] { - background: linear-gradient(180deg, #FFFFFF 0%, #E9F4F4 100%); - } - [purpose='bottom-cloud-city-banner'] { - background: linear-gradient(180deg, #E9F4F4 0%, #FFFFFF 100%); - img { - width: 100%; - } - } - - @media (min-width: 1200px) { - [purpose='page-container'] { - padding-left: 120px; - padding-right: 120px; - } - } - - @media (max-width: 1200px) { - [purpose='logos'] { - height: 58px; - margin-left: auto; - margin-right: auto; - img { - margin-top: 23.5px; - margin-bottom: 23.5px; - padding-left: 25px; - padding-right: 25px; - } - [purpose='snowflake-logo'] { - height: 21px; - } - [purpose='wayfair-logo'] { - height: 23px; - } - [purpose='uber-logo'] { - height: 20px; - } - [purpose='atlassian-logo'] { - height: 16px; - } - [purpose='segment-logo'] { - height: 22px; - } - } - } - - @media (max-width: 991px) { - - [purpose='button-row'] { - margin-right: auto; - margin-left: auto; - } - - [purpose='hero-background'] { - padding-top: 120px; - padding-bottom: 80px; - padding-left: 0px; - padding-right: 0px; - } - - [purpose='logos'] { - height: 58px; - margin-left: 20px; - margin-right: 20px; - img { - margin-top: 17.5px; - margin-bottom: 17.5px; - padding-left: 10px; - padding-right: 10px; - } - } - } - - @media (max-width: 768px) { - - h1 { - font-size: 48px; - line-height: 54px; - } - - [purpose='page-container'] { - padding-left: 40px; - padding-right: 40px; - } - - [purpose='checklists'] { - margin-bottom: 40px; - } - - [purpose='feature'] { - margin-top: 80px; - } - - [purpose='feature-image'], [purpose='large-feature-image'] { - margin-bottom: 40px; - img { - max-height: 100%; - max-width: 100%; - } - } - - [purpose='hero-background'] { - padding-top: 60px; - padding-bottom: 60px; - padding-left: 20px; - padding-right: 20px; - } - - [purpose='hero-text'] { - text-align: center; - max-width: 480px; - } - - [purpose='tweets-container'] { - padding-top: 80px; - padding-bottom: 80px; - padding-left: 40px; - padding-right: 40px; - } - - [purpose='logos'] { - height: auto; - } - } - - @media (max-width: 575px) { - - [purpose='button-row'] { - max-width: 100%; - [purpose='cta-button'] { - margin-right: 0px; - width: 100%; - margin-bottom: 24px; - } - } - - [purpose='feature'] { - margin-top: 60px; - } - - [purpose='page-container'] { - padding-left: 20px; - padding-right: 20px; - } - - [purpose='tweets-container'] { - padding-left: 20px; - padding-right: 20px; - } - - [parasails-component='scrollable-tweets'] [purpose='tweets'] { - margin-top: 40px; - } - - } - -} diff --git a/website/assets/styles/pages/imagine/jamf-alternative.less b/website/assets/styles/pages/imagine/jamf-alternative.less deleted file mode 100644 index 62fa8e1c14..0000000000 --- a/website/assets/styles/pages/imagine/jamf-alternative.less +++ /dev/null @@ -1,347 +0,0 @@ -#jamf-alternative { - - h1 { - font-weight: 800; - font-size: 64px; - line-height: 76px; - } - h2 { - font-weight: 800; - font-size: 32px; - line-height: 38px; - margin-bottom: 40px; - } - h3 { - font-weight: 800; - font-size: 24px; - line-height: 32px; - } - h4 { - font-family: 'Roboto Mono'; - font-style: normal; - font-weight: 400; - font-size: 18px; - line-height: 24px; - color: @core-fleet-black-75; - margin-bottom: 4px; - } - p { - font-size: 16px; - line-height: 24px; - } - - [purpose='hero-background'] { - background: linear-gradient(180deg, #E8F1F7 3.37%, #FFFFFF 60%); - padding-top: 120px; - padding-bottom: 40px; - } - [purpose='hero-container'] { - max-width: 1200px; - } - [purpose='hero-text'] { - text-align: center; - max-width: 940px; - padding: 0 40px; - h1 { - margin-bottom: 32px; - } - p { - font-size: 18px; - line-height: 27px; - max-width: 640px; - margin: 0 auto 32px; - } - } - - [purpose='button-row'] { - a { - font-weight: 700; - font-size: 16px; - line-height: 24px; - } - [purpose='cta-button'] { - cursor: pointer; - margin-right: 32px; - background: @core-vibrant-red; - border-radius: 8px; - padding-left: 32px; - padding-right: 32px; - height: 48px; - display: flex; - justify-content: center; - align-items: center; - color: #FFF; - position: relative; - text-decoration: none; - } - [purpose='cta-button']::before { - background: linear-gradient(180deg, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0) 100%); - opacity: 1; - content: ' '; - position: absolute; - top: 0; - left: -5px; - width: 50%; - height: 100%; - transform: skew(-10deg); - transition: left 0.5s ease-in, opacity 0.50s ease-in, width 0.5s ease-in; - } - [purpose='cta-button']:hover:before { - opacity: 0; - left: 160px; - width: 110%; - } - - [purpose='animated-arrow-button-red'] { - display: inline; - padding-right: 40px; - cursor: pointer; - position: relative; - width: fit-content; - min-width: 125px; - font-weight: bold; - user-select: none; - transition: 0.2s ease-in-out; - -o-transition: 0.2s ease-in-out; - -ms-transition: 0.2s ease-in-out; - -moz-transition: 0.2s ease-in-out; - -webkit-transition: 0.2s ease-in-out; - color: @core-fleet-black; - text-decoration: none; - &:after { - content: url('/images/arrow-right-red-16x16@2x.png'); - transform: scale(0.5); - position: absolute; - top: -5px; - left: 80%; // <--- here - transition: 0.2s ease-in-out; - -o-transition: 0.2s ease-in-out; - -ms-transition: 0.2s ease-in-out; - -moz-transition: 0.2s ease-in-out; - -webkit-transition: 0.2s ease-in-out; - /* opacity: 0; */ - } - &:hover:after { - left: 82%; // <--- here - transition: 0.2s ease-in-out; - -o-transition: 0.2s ease-in-out; - -ms-transition: 0.2s ease-in-out; - -moz-transition: 0.2s ease-in-out; - -webkit-transition: 0.2s ease-in-out; - /* opacity:1; */ - } - } - } - - [purpose='page-container'] { - padding-left: 40px; - padding-right: 40px; - padding-bottom: 120px; - } - - [purpose='feature'] { - margin-top: 120px; - max-width: 960px; - h3 { - margin-bottom: 24px; - } - div { - max-width: 480px; - } - } - - [purpose='feature-image'] { - img { - max-height: 300px; - } - } - [purpose='large-feature-image'] { - img { - max-height: 250px; - } - } - - [purpose='checklists'] { - margin-top: 40px; - margin-bottom: 80px; - } - [purpose='checklist'] { - p { - padding-left: 28px; - text-indent: -28px; - } - p::before { - content: ' '; - background-image: url('/images/icon-checkmark-circle.svg'); - background-size: 16px 16px; - display: inline-block; - position: relative; - top: 2px; - margin-right: 12px; - width: 16px; - height: 16px; - } - } - - [purpose='tweets-container'] { - padding-top: 120px; - padding-bottom: 120px; - } - - [purpose='bottom-gradient'] { - background: linear-gradient(180deg, #FFFFFF 0%, #E9F4F4 100%); - } - [purpose='bottom-cloud-city-banner'] { - background: linear-gradient(180deg, #E9F4F4 0%, #FFFFFF 100%); - img { - width: 100%; - } - } - - @media (min-width: 1200px) { - [purpose='page-container'] { - padding-left: 120px; - padding-right: 120px; - } - } - - @media (max-width: 1200px) { - [purpose='logos'] { - height: 58px; - margin-left: auto; - margin-right: auto; - img { - margin-top: 23.5px; - margin-bottom: 23.5px; - padding-left: 25px; - padding-right: 25px; - } - [purpose='snowflake-logo'] { - height: 21px; - } - [purpose='wayfair-logo'] { - height: 23px; - } - [purpose='uber-logo'] { - height: 20px; - } - [purpose='atlassian-logo'] { - height: 16px; - } - [purpose='segment-logo'] { - height: 22px; - } - } - } - - @media (max-width: 991px) { - - [purpose='button-row'] { - margin-right: auto; - margin-left: auto; - } - - [purpose='hero-background'] { - padding-top: 120px; - padding-bottom: 80px; - padding-left: 0px; - padding-right: 0px; - } - - [purpose='logos'] { - height: 58px; - margin-left: 20px; - margin-right: 20px; - img { - margin-top: 17.5px; - margin-bottom: 17.5px; - padding-left: 10px; - padding-right: 10px; - } - } - } - - @media (max-width: 768px) { - - h1 { - font-size: 48px; - line-height: 54px; - } - - [purpose='page-container'] { - padding-left: 40px; - padding-right: 40px; - } - - [purpose='checklists'] { - margin-bottom: 40px; - } - - [purpose='feature'] { - margin-top: 80px; - } - - [purpose='feature-image'], [purpose='large-feature-image'] { - margin-bottom: 40px; - img { - max-height: 100%; - max-width: 100%; - } - } - - [purpose='hero-background'] { - padding-top: 60px; - padding-bottom: 60px; - padding-left: 20px; - padding-right: 20px; - } - - [purpose='hero-text'] { - text-align: center; - max-width: 480px; - } - - [purpose='tweets-container'] { - padding-top: 80px; - padding-bottom: 80px; - padding-left: 40px; - padding-right: 40px; - } - - [purpose='logos'] { - height: auto; - } - } - - @media (max-width: 575px) { - - [purpose='button-row'] { - max-width: 100%; - [purpose='cta-button'] { - margin-right: 0px; - width: 100%; - margin-bottom: 24px; - } - } - - [purpose='feature'] { - margin-top: 60px; - } - - [purpose='page-container'] { - padding-left: 20px; - padding-right: 20px; - } - - [purpose='tweets-container'] { - padding-left: 20px; - padding-right: 20px; - } - - [parasails-component='scrollable-tweets'] [purpose='tweets'] { - margin-top: 40px; - } - - } - -} diff --git a/website/assets/styles/pages/imagine/launch-party.less b/website/assets/styles/pages/imagine/launch-party.less deleted file mode 100644 index 80cd7bc838..0000000000 --- a/website/assets/styles/pages/imagine/launch-party.less +++ /dev/null @@ -1,192 +0,0 @@ -#launch-party { - padding-top: 60px; - padding-left: 40px; - padding-right: 40px; - max-width: 760px; - margin-left: auto; - margin-right: auto; - overflow-x: hidden; - h1 { - font-weight: 800; - font-size: 36px; - line-height: 40px; - margin-bottom: 12px; - } - h2 { - font-weight: 800; - font-size: 24px; - line-height: 34px; - } - p { - margin-bottom: 0; - font-size: 16px; - line-height: 24px; - color: #515774; - } - [purpose='map-container'] { - width: 100%; - overflow: hidden; - display: flex; - flex-direction: column; - align-content: center; - align-items: center; - border-radius: 8px; - } - [purpose='meet-the-team'] { - width: 100%; - img { - width: 210px; - } - } - [purpose='hero-text'] { - max-width: 420px; - p { - font-size: 18px; - } - } - [purpose='hero'] { - background-image: url('/images/rsa-map-hero-680x480@2x.png'); - height: 530px; - background-size: cover; - background-repeat: no-repeat; - background-position: center; - width: 100%; - margin-bottom: 40px; - border-radius: 8px; - } - [purpose='rsa-cta'] { - background: #F9FAFC; - padding: 16px 24px; - width: 100%; - border: 1px solid #E2E4EA; - border-radius: 8px; - display: flex; - flex-direction: column; - align-items: center; - margin-bottom: 40px; - text-align: center; - p { - font-weight: 700; - color: #515774; - } - } - [purpose='cta-button'] { - cursor: pointer; - font-size: 18px; - font-weight: 700; - margin-right: 32px; - background: @core-vibrant-red; - border-radius: 8px; - padding-left: 32px; - padding-right: 32px; - height: 48px; - display: flex; - justify-content: center; - align-items: center; - color: #FFF; - position: relative; - max-width: 420px; - width: 100%; - } - [purpose='cta-button']::before { - background: linear-gradient(180deg, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0) 100%); - opacity: 1; - content: ' '; - position: absolute; - top: 0; - left: -5px; - width: 60%; - height: 100%; - transform: skew(-10deg); - transition: left 0.5s ease-in, opacity 0.50s ease-in, width 0.5s ease-in; - } - [purpose='cta-button']:hover:before { - opacity: 0; - left: 160px; - width: 110%; - } - [purpose='section'] { - margin-bottom: 40px; - display: flex; - flex-direction: column; - align-items: center; - width: 100%; - } - [purpose='checklist'] { - margin-bottom: 40px; - p { - padding-left: 28px; - text-indent: -28px; - margin-bottom: 16px; - } - p::before { - content: ' '; - background-image: url('/images/icon-checkmark-circle.svg'); - background-size: 16px 16px; - display: inline-block; - position: relative; - top: 2px; - margin-right: 12px; - width: 16px; - height: 16px; - } - } - [purpose='modal-dialog'] { - padding-left: 15px; - padding-right: 15px; - max-width: 100%; - } - [purpose='modal-content'] { - margin-top: 40px; - padding-top: 80px; - padding-left: 80px; - padding-right: 80px; - padding-bottom: 120px; - margin-right: auto; - margin-left: auto; - max-width: 680px; - width: 100%; - } - [purpose='modal-form'] { - input { - border-radius: 6px; - height: 48px; - } - label { - font-weight: 700; - } - width: 100%; - } - [purpose='submit-button'] { - border-radius: 8px; - } - @media(max-width: 756px) { - [purpose='modal-content'] { - padding: 40px; - } - } - @media(max-width: 575px) { - padding-left: 20px; - padding-right: 20px; - [purpose='hero'] { - background-image: url('/images/rsa-map-hero-small-335x480@2x.png'); - background-size: cover; - } - } - @media(max-width: 459px) { - [purpose='meet-the-team'] { - img:first-of-type { - margin-top: 24px; - } - } - - } - @media(max-width: 375px) { - [purpose='modal-content'] { - padding: 40px 20px 40px 20px; - } - h1 { - font-size: 28px; - } - } -} diff --git a/website/assets/styles/pages/imagine/rapid-7-alternative.less b/website/assets/styles/pages/imagine/rapid-7-alternative.less deleted file mode 100644 index 9975598272..0000000000 --- a/website/assets/styles/pages/imagine/rapid-7-alternative.less +++ /dev/null @@ -1,347 +0,0 @@ -#rapid-7-alternative { - - h1 { - font-weight: 800; - font-size: 64px; - line-height: 76px; - } - h2 { - font-weight: 800; - font-size: 32px; - line-height: 38px; - margin-bottom: 40px; - } - h3 { - font-weight: 800; - font-size: 24px; - line-height: 32px; - } - h4 { - font-family: 'Roboto Mono'; - font-style: normal; - font-weight: 400; - font-size: 18px; - line-height: 24px; - color: @core-fleet-black-75; - margin-bottom: 4px; - } - p { - font-size: 16px; - line-height: 24px; - } - - [purpose='hero-background'] { - background: linear-gradient(180deg, #E8F1F7 3.37%, #FFFFFF 60%); - padding-top: 120px; - padding-bottom: 40px; - } - [purpose='hero-container'] { - max-width: 1200px; - } - [purpose='hero-text'] { - text-align: center; - max-width: 940px; - padding: 0 40px; - h1 { - margin-bottom: 32px; - } - p { - font-size: 18px; - line-height: 27px; - max-width: 640px; - margin: 0 auto 32px; - } - } - - [purpose='button-row'] { - a { - font-weight: 700; - font-size: 16px; - line-height: 24px; - } - [purpose='cta-button'] { - cursor: pointer; - margin-right: 32px; - background: @core-vibrant-red; - border-radius: 8px; - padding-left: 32px; - padding-right: 32px; - height: 48px; - display: flex; - justify-content: center; - align-items: center; - color: #FFF; - position: relative; - text-decoration: none; - } - [purpose='cta-button']::before { - background: linear-gradient(180deg, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0) 100%); - opacity: 1; - content: ' '; - position: absolute; - top: 0; - left: -5px; - width: 50%; - height: 100%; - transform: skew(-10deg); - transition: left 0.5s ease-in, opacity 0.50s ease-in, width 0.5s ease-in; - } - [purpose='cta-button']:hover:before { - opacity: 0; - left: 160px; - width: 110%; - } - - [purpose='animated-arrow-button-red'] { - display: inline; - padding-right: 40px; - cursor: pointer; - position: relative; - width: fit-content; - min-width: 125px; - font-weight: bold; - user-select: none; - transition: 0.2s ease-in-out; - -o-transition: 0.2s ease-in-out; - -ms-transition: 0.2s ease-in-out; - -moz-transition: 0.2s ease-in-out; - -webkit-transition: 0.2s ease-in-out; - color: @core-fleet-black; - text-decoration: none; - &:after { - content: url('/images/arrow-right-red-16x16@2x.png'); - transform: scale(0.5); - position: absolute; - top: -5px; - left: 80%; // <--- here - transition: 0.2s ease-in-out; - -o-transition: 0.2s ease-in-out; - -ms-transition: 0.2s ease-in-out; - -moz-transition: 0.2s ease-in-out; - -webkit-transition: 0.2s ease-in-out; - /* opacity: 0; */ - } - &:hover:after { - left: 82%; // <--- here - transition: 0.2s ease-in-out; - -o-transition: 0.2s ease-in-out; - -ms-transition: 0.2s ease-in-out; - -moz-transition: 0.2s ease-in-out; - -webkit-transition: 0.2s ease-in-out; - /* opacity:1; */ - } - } - } - - [purpose='page-container'] { - padding-left: 40px; - padding-right: 40px; - padding-bottom: 120px; - } - - [purpose='feature'] { - margin-top: 120px; - max-width: 960px; - h3 { - margin-bottom: 24px; - } - div { - max-width: 480px; - } - } - - [purpose='feature-image'] { - img { - max-height: 300px; - } - } - [purpose='large-feature-image'] { - img { - max-height: 250px; - } - } - - [purpose='checklists'] { - margin-top: 40px; - margin-bottom: 80px; - } - [purpose='checklist'] { - p { - padding-left: 28px; - text-indent: -28px; - } - p::before { - content: ' '; - background-image: url('/images/icon-checkmark-circle.svg'); - background-size: 16px 16px; - display: inline-block; - position: relative; - top: 2px; - margin-right: 12px; - width: 16px; - height: 16px; - } - } - - [purpose='tweets-container'] { - padding-top: 120px; - padding-bottom: 120px; - } - - [purpose='bottom-gradient'] { - background: linear-gradient(180deg, #FFFFFF 0%, #E9F4F4 100%); - } - [purpose='bottom-cloud-city-banner'] { - background: linear-gradient(180deg, #E9F4F4 0%, #FFFFFF 100%); - img { - width: 100%; - } - } - - @media (min-width: 1200px) { - [purpose='page-container'] { - padding-left: 120px; - padding-right: 120px; - } - } - - @media (max-width: 1200px) { - [purpose='logos'] { - height: 58px; - margin-left: auto; - margin-right: auto; - img { - margin-top: 23.5px; - margin-bottom: 23.5px; - padding-left: 25px; - padding-right: 25px; - } - [purpose='snowflake-logo'] { - height: 21px; - } - [purpose='wayfair-logo'] { - height: 23px; - } - [purpose='uber-logo'] { - height: 20px; - } - [purpose='atlassian-logo'] { - height: 16px; - } - [purpose='segment-logo'] { - height: 22px; - } - } - } - - @media (max-width: 991px) { - - [purpose='button-row'] { - margin-right: auto; - margin-left: auto; - } - - [purpose='hero-background'] { - padding-top: 120px; - padding-bottom: 80px; - padding-left: 0px; - padding-right: 0px; - } - - [purpose='logos'] { - height: 58px; - margin-left: 20px; - margin-right: 20px; - img { - margin-top: 17.5px; - margin-bottom: 17.5px; - padding-left: 10px; - padding-right: 10px; - } - } - } - - @media (max-width: 768px) { - - h1 { - font-size: 48px; - line-height: 54px; - } - - [purpose='page-container'] { - padding-left: 40px; - padding-right: 40px; - } - - [purpose='checklists'] { - margin-bottom: 40px; - } - - [purpose='feature'] { - margin-top: 80px; - } - - [purpose='feature-image'], [purpose='large-feature-image'] { - margin-bottom: 40px; - img { - max-height: 100%; - max-width: 100%; - } - } - - [purpose='hero-background'] { - padding-top: 60px; - padding-bottom: 60px; - padding-left: 20px; - padding-right: 20px; - } - - [purpose='hero-text'] { - text-align: center; - max-width: 480px; - } - - [purpose='tweets-container'] { - padding-top: 80px; - padding-bottom: 80px; - padding-left: 40px; - padding-right: 40px; - } - - [purpose='logos'] { - height: auto; - } - } - - @media (max-width: 575px) { - - [purpose='button-row'] { - max-width: 100%; - [purpose='cta-button'] { - margin-right: 0px; - width: 100%; - margin-bottom: 24px; - } - } - - [purpose='feature'] { - margin-top: 60px; - } - - [purpose='page-container'] { - padding-left: 20px; - padding-right: 20px; - } - - [purpose='tweets-container'] { - padding-left: 20px; - padding-right: 20px; - } - - [parasails-component='scrollable-tweets'] [purpose='tweets'] { - margin-top: 40px; - } - - } - -} diff --git a/website/assets/styles/pages/imagine/unused-software.less b/website/assets/styles/pages/imagine/unused-software.less deleted file mode 100644 index 0109361699..0000000000 --- a/website/assets/styles/pages/imagine/unused-software.less +++ /dev/null @@ -1,367 +0,0 @@ -#unused-software { - - h1 { - font-weight: 800; - font-size: 64px; - line-height: 76px; - } - h2 { - font-weight: 800; - font-size: 32px; - line-height: 38px; - margin-bottom: 40px; - } - h3 { - font-weight: 800; - font-size: 24px; - line-height: 32px; - } - h4 { - color: @core-fleet-black-75; - font-weight: 500; - font-size: 18px; - line-height: 24px; - /* Text gradient */ - background: linear-gradient(90deg, #FF5C83 0%, #6A67FE 100%); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - text-fill-color: transparent; - display: inline-block; - /* End text gradient */ - } - p { - font-size: 16px; - line-height: 24px; - } - - [purpose='hero-background'] { - background: linear-gradient(180deg, #E8F1F7 3.37%, #FFFFFF 60%); - padding-top: 120px; - padding-bottom: 40px; - } - [purpose='hero-container'] { - max-width: 1200px; - } - [purpose='hero-text'] { - text-align: center; - max-width: 940px; - padding: 0 40px; - h1 { - margin-bottom: 32px; - } - p { - font-size: 18px; - line-height: 27px; - max-width: 640px; - margin: 0 auto 32px; - } - } - - [purpose='button-row'] { - a { - font-weight: 700; - font-size: 16px; - line-height: 24px; - } - [purpose='cta-button'] { - cursor: pointer; - margin-right: 32px; - background: @core-vibrant-red; - border-radius: 8px; - padding-left: 32px; - padding-right: 32px; - height: 48px; - display: flex; - justify-content: center; - align-items: center; - color: #FFF; - position: relative; - text-decoration: none; - } - [purpose='cta-button']::before { - background: linear-gradient(180deg, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0) 100%); - opacity: 1; - content: ' '; - position: absolute; - top: 0; - left: -5px; - width: 50%; - height: 100%; - transform: skew(-10deg); - transition: left 0.5s ease-in, opacity 0.50s ease-in, width 0.5s ease-in; - } - [purpose='cta-button']:hover:before { - opacity: 0; - left: 160px; - width: 110%; - } - - [purpose='animated-arrow-button-red'] { - display: inline; - padding-right: 40px; - cursor: pointer; - position: relative; - width: fit-content; - min-width: 125px; - font-weight: bold; - user-select: none; - transition: 0.2s ease-in-out; - -o-transition: 0.2s ease-in-out; - -ms-transition: 0.2s ease-in-out; - -moz-transition: 0.2s ease-in-out; - -webkit-transition: 0.2s ease-in-out; - color: @core-fleet-black; - text-decoration: none; - &:after { - content: url('/images/arrow-right-red-16x16@2x.png'); - transform: scale(0.5); - position: absolute; - top: -5px; - left: 80%; // <--- here - transition: 0.2s ease-in-out; - -o-transition: 0.2s ease-in-out; - -ms-transition: 0.2s ease-in-out; - -moz-transition: 0.2s ease-in-out; - -webkit-transition: 0.2s ease-in-out; - /* opacity: 0; */ - } - &:hover:after { - left: 82%; // <--- here - transition: 0.2s ease-in-out; - -o-transition: 0.2s ease-in-out; - -ms-transition: 0.2s ease-in-out; - -moz-transition: 0.2s ease-in-out; - -webkit-transition: 0.2s ease-in-out; - /* opacity:1; */ - } - } - } - - [purpose='page-container'] { - padding-left: 40px; - padding-right: 40px; - padding-bottom: 120px; - } - - [purpose='feature'] { - margin-top: 120px; - max-width: 960px; - h3 { - margin-bottom: 24px; - } - div { - max-width: 480px; - } - } - - [purpose='feature-image'] { - height: 100%; - img { - max-height: 300px; - } - } - - [purpose='checklists'] { - margin-top: 40px; - margin-bottom: 80px; - } - [purpose='checklist'] { - p { - padding-left: 28px; - text-indent: -28px; - } - p::before { - content: ' '; - background-image: url('/images/icon-checkmark-circle.svg'); - background-size: 16px 16px; - display: inline-block; - position: relative; - top: 2px; - margin-right: 12px; - width: 16px; - height: 16px; - } - } - - [purpose='tweets-container'] { - padding-top: 120px; - padding-bottom: 120px; - background-color: @ui-off-white; - } - - [purpose='logos'] { - height: 80px; - max-width: 1200px; - margin-left: auto; - margin-right: auto; - img { - margin-top: 23.5px; - margin-bottom: 23.5px; - padding-left: 35px; - padding-right: 35px; - } - [purpose='snowflake-logo'] { - height: 30px; - } - [purpose='wayfair-logo'] { - height: 33px; - } - [purpose='uber-logo'] { - height: 29px; - } - [purpose='atlassian-logo'] { - height: 22px; - } - [purpose='segment-logo'] { - height: 32px; - } - } - - @media (min-width: 1200px) { - [purpose='page-container'] { - padding-left: 120px; - padding-right: 120px; - } - } - - @media (max-width: 1200px) { - [purpose='logos'] { - height: 58px; - margin-left: auto; - margin-right: auto; - img { - margin-top: 23.5px; - margin-bottom: 23.5px; - padding-left: 25px; - padding-right: 25px; - } - [purpose='snowflake-logo'] { - height: 21px; - } - [purpose='wayfair-logo'] { - height: 23px; - } - [purpose='uber-logo'] { - height: 20px; - } - [purpose='atlassian-logo'] { - height: 16px; - } - [purpose='segment-logo'] { - height: 22px; - } - } - } - - @media (max-width: 991px) { - - [purpose='button-row'] { - margin-right: auto; - margin-left: auto; - } - - [purpose='hero-background'] { - padding-top: 120px; - padding-bottom: 80px; - padding-left: 0px; - padding-right: 0px; - } - - [purpose='logos'] { - height: 58px; - margin-left: 20px; - margin-right: 20px; - img { - margin-top: 17.5px; - margin-bottom: 17.5px; - padding-left: 10px; - padding-right: 10px; - } - } - } - - @media (max-width: 768px) { - - h1 { - font-size: 48px; - line-height: 54px; - } - - [purpose='page-container'] { - padding-left: 40px; - padding-right: 40px; - } - - [purpose='checklists'] { - margin-bottom: 40px; - } - - [purpose='feature'] { - margin-top: 80px; - } - - [purpose='feature-image'] { - margin-bottom: 40px; - img { - max-height: 100%; - max-width: 100%; - } - } - - [purpose='hero-background'] { - padding-top: 60px; - padding-bottom: 60px; - padding-left: 20px; - padding-right: 20px; - } - - [purpose='hero-text'] { - text-align: center; - max-width: 480px; - } - - [purpose='tweets-container'] { - padding-top: 80px; - padding-bottom: 80px; - padding-left: 40px; - padding-right: 40px; - } - - [purpose='logos'] { - height: auto; - } - } - - @media (max-width: 575px) { - - [purpose='button-row'] { - max-width: 100%; - [purpose='cta-button'] { - margin-right: 0px; - width: 100%; - margin-bottom: 24px; - } - } - - [purpose='feature'] { - margin-top: 60px; - } - - [purpose='page-container'] { - padding-left: 20px; - padding-right: 20px; - } - - [purpose='tweets-container'] { - padding-left: 20px; - padding-right: 20px; - } - - [parasails-component='scrollable-tweets'] [purpose='tweets'] { - margin-top: 40px; - } - - } - -} diff --git a/website/config/routes.js b/website/config/routes.js index be70757038..cb50097c11 100644 --- a/website/config/routes.js +++ b/website/config/routes.js @@ -287,40 +287,6 @@ module.exports.routes = { } }, - - // ╦╔╦╗╔═╗╔═╗╦╔╗╔╔═╗ β”Œβ”€β”¬ β”Œβ”€β”β”Œβ”β”Œβ”Œβ”¬β”β”¬β”Œβ”β”Œβ”Œβ”€β” β”Œβ”€β”β”Œβ”€β”β”Œβ”€β”β”Œβ”€β”β”Œβ”€β”β”€β” - // ║║║║╠═╣║ ╦║║║║║╣ β”‚ β”‚ β”œβ”€β”€β”‚β”‚β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ ┬ β”œβ”€β”˜β”œβ”€β”€β”‚ β”¬β”œβ”€ └─┐ β”‚ - // β•©β•© β•©β•© β•©β•šβ•β•β•©β•β•šβ•β•šβ•β• β””β”€β”΄β”€β”˜β”΄ β”΄β”˜β””β”˜β”€β”΄β”˜β”΄β”˜β””β”˜β””β”€β”˜ β”΄ β”΄ β”΄β””β”€β”˜β””β”€β”˜β””β”€β”˜β”€β”˜ - 'GET /imagine/unused-software': { action: 'imagine/view-unused-software' }, - 'GET /imagine/higher-education': { - action: 'imagine/view-higher-education', - locals: { - pageTitleForMeta: 'Fleet for higher education', - pageDescriptionForMeta: 'Automate security workflows in a single application by creating or installing policies to identify which devices comply with your security guidelines.', - } - }, - 'GET /imagine/rapid-7-alternative': { - action: 'imagine/view-rapid-7-alternative', - locals: { - pageTitleForMeta: 'An open-source alternative to Rapid7', - pageDescriptionForMeta: 'Simplify vulnerability management with Fleet, an open-source platform with superior visibility.', - } - }, - 'GET /imagine/defcon-31': { - action: 'imagine/view-defcon-31', - locals: { - pageTitleForMeta: 'Fleet at DefCon 31', - pageDescriptionForMeta: 'Find Fleet at DefCon and get a custom tee shirt.', - } - }, - 'GET /imagine/jamf-alternative': { - action: 'imagine/view-jamf-alternative', - locals: { - pageTitleForMeta: 'An open-source alternative to Jamf', - pageDescriptionForMeta: 'Simplify vulnerability management with Fleet, an open-source platform with superior visibility.', - } - }, - // ╦ ╔═╗╔═╗╔═╗╔═╗╦ ╦ ╦═╗╔═╗╔╦╗╦╦═╗╔═╗╔═╗╔╦╗╔═╗ // β•‘ β•‘β•£ β•‘ ╦╠═╣║ β•šβ•¦β• ╠╦╝║╣ ║║║╠╦╝║╣ β•‘ β•‘ β•šβ•β•— // β•©β•β•β•šβ•β•β•šβ•β•β•© β•©β•šβ•β• β•© β•©β•šβ•β•šβ•β•β•β•©β•β•©β•©β•šβ•β•šβ•β•β•šβ•β• β•© β•šβ•β• diff --git a/website/views/pages/imagine/defcon-31.ejs b/website/views/pages/imagine/defcon-31.ejs deleted file mode 100644 index f66f1ea71b..0000000000 --- a/website/views/pages/imagine/defcon-31.ejs +++ /dev/null @@ -1,71 +0,0 @@ -
-
-
-
-
-
-
-

Grab drinks with the Fleet technical team

-
- Alex Mitchell - CRO & Software Engineer - Zach Wasserman - Co-founder of Fleet & Co-creator, osquery - Jarod Reyes - VP of Marketing & Software Engineer -
-
-
-

Fleet happy hour at Beer Park

-
- A map showing the location of Beer Park -
-
- Easy walking distance from DefCon & Black Hat -
-
-
-

Join us at Beer Park

-

Aug 9, 2023 | 7 - 9pm PT

- RSVP here -
-
-
- An illustration of the room where Fleet's workshop will be -

Fleet Workshop at Wall of Sheep

-

Aug 10-12, 2023 | 9am - 8pm PT

-

In the "Wall of Sheep" Packet-Hacking Village at Caesar's Forum.

-
-

The future and present rely on your ability to decode the mess in the machines.

-
-

The lore

-

In the year 2057, Earth was shrouded in a cloudy opaque darkness, not by the forces of nature but by an artificial plague. Words lost their meanings, syntax tangled like a complex web, and conversations turned into garbled chaos. The culprit? Advanced AI bots, an unfathomable force that had manipulated the global linguistic network, turning our life's lexicon into a cryptic enigma.

- -

This was no ordinary darkness. It was an intangible blight, a dark nebula woven from chaotic words and scrambled semantics. Entire societies were locked in incomprehension. Economies collapsed as contracts became gibberish, international diplomacy failed as treaties turned into nonsensical paragraphs. Even simple, everyday communication became impossible.

- -

In this time of despair, emerged an unlikely hero, Jules Morse.

- -

Your job as our hero Jules, is to plunge into the depths of these machines under your stewardship and find the words that were hidden there by past oracles, those who would show us the way out of this miasma.

- -

What you'll learn:

- -
-

Learn how to use Fleet

-

Learn how to query machines using osquery

-

Learn how to break queries

-

Learn how to spot vulnerabilities in hosts

-

Learn how to find active processies on machines by user

-

Discover env variables on a remote machine

-

Inspect user activity remotely

-

Inspect installed software remotely

-
-
- -

See you in Vegas!

-
- -
- Fleet logo - Follow Fleet on Twitter - Join the osquery Slack community -
-
-
-<%- /* Expose server-rendered data as window.SAILS_LOCALS :: */ exposeLocalsToBrowser() %> diff --git a/website/views/pages/imagine/higher-education.ejs b/website/views/pages/imagine/higher-education.ejs deleted file mode 100644 index 5310abb0c6..0000000000 --- a/website/views/pages/imagine/higher-education.ejs +++ /dev/null @@ -1,100 +0,0 @@ -
- -
- -
-
-

Visibility and compliance in a single platform

-

Higher education meets simplified security

-

Automate security workflows in a single application by creating or installing policies to identify which devices comply with your security guidelines.

- -
-
- -
- -
-
-
-

Out-of-the-box CIS benchmark policies

-

Colleges are often subject to strict regulatory compliance requirements. With Center for Internet Security (CIS) compliance policies in Fleet, cybersecurity best practices are at your fingertips.

-
-

Out-of-the-box policies for Mac, Windows, and Linux.

-

Fleet will keep these policies updated for you.

-

Identify which devices are failing the most so you can prioritize your work.

-
-
-
- higher-education feature image -
-
- -
-
- higher-education feature image -
-
-

Asset management

-

Colleges have a large number of devices on their network, including laptops, desktops, servers, and mobile devices. Fleet and osquery can help colleges track these devices and ensure that they are properly managed.

-
-

Troubleshoot issues with detailed configuration and performance details in real time on any operating system.

-

Easily manage any amount of software applications in your fleet with visibility into installed applications and their vulnerabilities.

-

Save money on software licensing fees by identifying underutilized devices and applications with an unused software report.

-
-
-
- -
-
-

Automations in response to failing policies

-

Fleet Premium comes out-of-the-box ready for audits. Integrate with the security tools and services in your stack. Get answers immediately, even when you’re not looking.

-
-

Configurable scan intervals for policies.

-

Trigger webhooks and integrate with Jira, Zendesk, or Tines.

-

Encourage end-user self-remediation with Fleet Desktop.

-
-
-
- higher-education feature image -
-
- - - -
- -
-
-
-

Don’t know osquery?

-

Dedicated support from osquery experts

-

Osquery is the open-source agent that powers Fleet. And we have the most osquery experts around. We’ll help you realize the potential of this tool for your organization.

-
-
- - -
- -
-

Open-source device management

-

Think for yourself

- -
-
-
-
- A glass city floating on top of fluffy white clouds - A glass city floating on top of fluffy white clouds - A glass city floating on top of fluffy white clouds -
-
-<%- /* Expose server-rendered data as window.SAILS_LOCALS :: */ exposeLocalsToBrowser() %> diff --git a/website/views/pages/imagine/jamf-alternative.ejs b/website/views/pages/imagine/jamf-alternative.ejs deleted file mode 100644 index 8f002393b7..0000000000 --- a/website/views/pages/imagine/jamf-alternative.ejs +++ /dev/null @@ -1,74 +0,0 @@ -
- -
- -
-
-

Device management built for IT, security approved

-

Fleet brings GitOps to MDM

-

Bringh-your-own MDM. Enjoy enterprise-ready open-source MDM and leverage the best of DevOps and GitOps inside a full-featured Macbook MDM.

- -
-
-
- -
-
-
-

Finally a transparent and open-source MDM.

-

Don't waste money buying new Protection plans from your MDM provider.

-
-
- A Fleet orb scanning a Laptop -
-
- -
-
- A bento box featuring all the tools Fleet can bring together -
-
-

JAMF and Intune are never perfect out of the box, so why not bring your own MDM?

-

A transparent and programmable MDM to build the solution that's best for your team.

-
-
- - - -
- -
-
-
-

Don’t know osquery?

-

Dedicated support from osquery experts

-

Osquery is the open-source agent that powers Fleet. And we have the most osquery experts around. We’ll help you realize the potential of this tool for your organization.

-
-
- - -
- -
-

Open-source device management

-

Lighter than air

- -
-
-
-
- A glass city floating on top of fluffy white clouds - A glass city floating on top of fluffy white clouds - A glass city floating on top of fluffy white clouds -
-
-<%- /* Expose server-rendered data as window.SAILS_LOCALS :: */ exposeLocalsToBrowser() %> diff --git a/website/views/pages/imagine/launch-party.ejs b/website/views/pages/imagine/launch-party.ejs deleted file mode 100644 index 74112ab165..0000000000 --- a/website/views/pages/imagine/launch-party.ejs +++ /dev/null @@ -1,109 +0,0 @@ -
-
-
-
-

The path to a better
MDM starts here

-

Introducing an MDM your security team will love

-
-
-
-

Launch party at Press Club

-

April 27, 2023 | 5 - 8pm PT

- Join the wait list -
-

Join us in celebrating Fleet’s new MDM features

-
-

Report on disk encryption status

-

User-initiated device enrollment

-

Remotely enforce OS settings

-

Zero-touch device setup

-

Remotely update OS version

-

Low-level MDM commands

-

Keep OS up to date through reminders

-

Encrypt computer hard disks

-

Manage queued MDM commands

-

Remotely lock and wipe computers

-

Trigger a workflow based on a failing policy

-

Update apps on computers

-
-
-

Enjoy drinks and talk to the Fleet team

-
- Mike McNeil - Co-founder, CEO Fleet - Zach Wasserman - Co-founder of Fleet & Co-creator, osquery - Reid Christian - General Partner, CRV -
-
-
-
-

Easy walking distance from RSA

-
-
- A map showing the location of Press Club -
-
-
-

Launch party at Press Club

-

April 27, 2023 | 5 - 8pm PT

- Join the wait list -
-
-
- Fleet logo - Follow Fleet on Twitter - Join the osquery Slack community -
-
- - -
- -
- -
-
- - -
Please enter your first name.
-
-
- - -
Please enter your last name.
-
-
-
- - -
This doesn’t appear to be a valid email address
-
-
- - -
This doesn’t appear to be a valid email address
-
-
- - -
Please enter your job title. -
-
- -
- - Join the wait list - RSVP - -
-
-
-
-

You're all set!

-

A Fleet team member will reach out via email with more information.

-
-
-
-
-<%- /* Expose server-rendered data as window.SAILS_LOCALS :: */ exposeLocalsToBrowser() %> diff --git a/website/views/pages/imagine/rapid-7-alternative.ejs b/website/views/pages/imagine/rapid-7-alternative.ejs deleted file mode 100644 index 3cd081e0db..0000000000 --- a/website/views/pages/imagine/rapid-7-alternative.ejs +++ /dev/null @@ -1,115 +0,0 @@ -
- -
- -
-
-

A Rapid7 alternative for agent-based vulnerability management

-

Simplify vulnerability management

-

Fleet, an open-source security platform, empowers you to take control of vulnerability management for your organization. Designed with modern DevOps practices in mind, Fleet offers superior visibility, rapid response capabilities, and seamless integration with your existing workflows.

- -
-
- -
- -
-
-
-

Open source advantage

-

Fleet is built on the belief that the best software is open and transparent. Being open-source allows for continuous innovation and adaptability in an ever-evolving threat landscape. Transparency in operation means you'll always know what your vulnerability management tool is doing.

-
-

Global developer community fosters early vulnerability detection.

-

Transparent operations for enhanced trust.

-

Avoid vendor lock-ins with open source.

-
-
-
- rapid-7-alternative feature image -
-
- -
-
- rapid-7-alternative feature image -
-
-

Superior visibility and response

-

Fleet enables high-frequency querying, providing real-time visibility into your infrastructure's vulnerabilities. Fleet's flexible open-source nature allows for faster response times and remediation, by allowing you to surface alerts where your team is already looking.

-
-

Real-time visibility into infrastructure vulnerabilities.

-

Faster response and remediation times.

-

Mitigate threats more immediately than Rapid7.

-
-
-
- -
-
-

Flexibility and extensibility

-

Fleet's open-source architecture offers unmatched flexibility. With Fleet, developers can pipe data to platforms they are already using and generate reports in familiar environments. Whether you want to integrate with a specific IT service management tool or push osquery data into a SIEM of your choice, Fleet enables it all. In comparison, Rapid7 operates within a more confined ecosystem, limiting your options and flexibility.

-
-

Pipe data to platforms your team uses.

-

Integrates seamlessly with IT service tools

-

Open-source unlocks boundless functional possibilities.

-
-
-
- rapid-7-alternative feature image -
-
- -
-
- rapid-7-alternative feature image -
-
-

Value-driven alternative

-

Despite offering advanced features and capabilities, Fleet remains an economically sound alternative to Rapid7. As we believe in delivering value without imposing exorbitant costs, Fleet emerges as a choice that respects your budget constraints without compromising on performance and functionality.

-
-

Advanced features at an economical cost.

-

Affordable solution with no feature compromise.

-

Powerful vulnerability management that respects budgets.

-
-
-
- - - -
- -
-
-
-

Don’t know osquery?

-

Dedicated support from osquery experts

-

Osquery is the open-source agent that powers Fleet. And we have the most osquery experts around. We’ll help you realize the potential of this tool for your organization.

-
-
- - -
- -
-

Open-source device management

-

Lighter than air

- -
-
-
-
- A glass city floating on top of fluffy white clouds - A glass city floating on top of fluffy white clouds - A glass city floating on top of fluffy white clouds -
-
-<%- /* Expose server-rendered data as window.SAILS_LOCALS :: */ exposeLocalsToBrowser() %> diff --git a/website/views/pages/imagine/unused-software.ejs b/website/views/pages/imagine/unused-software.ejs deleted file mode 100644 index 5f62c7684b..0000000000 --- a/website/views/pages/imagine/unused-software.ejs +++ /dev/null @@ -1,98 +0,0 @@ -
- -
- -
-
-

Harness the power of Fleet's superior visibility

-

Discover unused software licenses and optimize your IT budget

-

Fleet, the leading open-source, flexible device management solution, offers unprecedented visibility into your IT infrastructure, making it the ideal tool to discover and manage unused software licenses. This capability is essential to unlocking more IT budget, enhancing security, and ultimately improving the employee experience.

- -
-
- -
- -
- -
-
-

Unmatched visibility

-

Fleet gives real-time visibility into your infrastructure, offering detailed views of installed software via high-frequency querying. An open-source solution, it promises unparalleled operational transparency.

-
-

Real-time view of all software installations.

-

Open-source transparency ensures you know what's happening.

-

An MDM that can check that your profiles are accurate

-
-
-
- unused-software feature image -
-
- -
-
- unused-software feature image -
-
-

Optimizing IT budgets

-

Fleet swiftly identifies unused software licenses, allowing you to optimize your IT budget by only paying for what you use. It's a cost-effective solution, promising both immediate and long-term financial benefits.

-
-

Identify unused licenses for budget optimization.

-

Unlock budget by discovering how many empty seats you are paying for

-

Create scheduled queries that give you a daily snapshot of software

-
-
-
- -
-
-

Enhancing employee experience

-

Fleet helps streamline your software environment by detecting unused applications, creating a clutter-free and productive tech experience for your team, thus boosting job satisfaction.

-
-

Say yes to more software by reducing overspend

-

Streamline software environment for better productivity.

-

Eliminate unnecessary clutter to enhance job satisfaction.

-
-
-
- feature image C -
-
- - - -
- -
-
-

Don’t know osquery?

-

Dedicated support from osquery experts

- -

Osquery is the open-source agent that powers Fleet. And we have the most osquery experts around. We’ll help you realize the potential of this tool for your organization.

-
-
- -
- -
- -
-
- The Snowflake logo - The Wayfair logo - The Uber logo - The Atlassian logo - The Twilio Segment logo -
-
- -
- -<%- /* Expose server-rendered data as window.SAILS_LOCALS :: */ exposeLocalsToBrowser() %> From 58db642f0a350f4e89ce5c5098373c1eb0716502 Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 4 Apr 2024 21:05:00 -0500 Subject: [PATCH 42/60] Website: remove /imagine policy and route. (#18076) Changes: - Removed `/imagine/*` policies. --- website/config/policies.js | 3 --- website/config/routes.js | 1 - 2 files changed, 4 deletions(-) diff --git a/website/config/policies.js b/website/config/policies.js index 7abc4d3b4b..6f9d53471a 100644 --- a/website/config/policies.js +++ b/website/config/policies.js @@ -13,9 +13,6 @@ module.exports.policies = { '*': 'is-logged-in', 'admin/*': 'is-super-admin', - // Bypass the `is-logged-in` policy for experiments, such as temporary landing pages. - 'imagine/*': true, - // Bypass the `is-logged-in` policy for: 'entrance/*': true, 'webhooks/*': true, diff --git a/website/config/routes.js b/website/config/routes.js index cb50097c11..12e13b3a7d 100644 --- a/website/config/routes.js +++ b/website/config/routes.js @@ -539,7 +539,6 @@ module.exports.routes = { 'POST /api/v1/create-vanta-authorization-request': { action: 'create-vanta-authorization-request' }, 'POST /api/v1/deliver-mdm-beta-signup': { action: 'deliver-mdm-beta-signup' }, 'POST /api/v1/deliver-apple-csr ': { action: 'deliver-apple-csr', csrf: false}, - 'POST /api/v1/deliver-launch-party-signup': { action: 'imagine/deliver-launch-party-signup' }, 'POST /api/v1/deliver-mdm-demo-email': { action: 'deliver-mdm-demo-email' }, 'POST /api/v1/admin/provision-sandbox-instance-and-deliver-email': { action: 'admin/provision-sandbox-instance-and-deliver-email' }, 'POST /api/v1/deliver-talk-to-us-form-submission': { action: 'deliver-talk-to-us-form-submission' }, From 51c59e59b88cf92c1b73cca9e7b67bdb3258f623 Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 4 Apr 2024 22:57:45 -0500 Subject: [PATCH 43/60] Website: update personalization (#18075) Changes: - updated the custom hook to support clearing the `primaryBuyingSituation` with a query string (`?utm_content=clear`) - Updated the pricing page to pre-select a pricing table mode based on a user's `primaryBuyingSituation` - reordered content on and updated the /support page to personalize/hide community slack links based on a user's `primaryBuyingSituation` - Updated the meta description of the product category landing pages - updated personalization based on `primaryBuyingSituation` on the /contact page. - Updated the title of the /queries page based on the user's `primaryBuyingSituation` - Added personalization to the product category landing pages. --- website/api/hooks/custom/index.js | 4 +- website/assets/js/pages/pricing.page.js | 8 +- website/assets/styles/pages/support.less | 8 +- website/config/routes.js | 10 +-- website/views/pages/contact.ejs | 10 ++- website/views/pages/device-management.ejs | 2 +- website/views/pages/endpoint-ops.ejs | 2 +- website/views/pages/query-library.ejs | 5 +- website/views/pages/support.ejs | 86 ++++++++++--------- .../views/pages/vulnerability-management.ejs | 2 +- 10 files changed, 76 insertions(+), 61 deletions(-) diff --git a/website/api/hooks/custom/index.js b/website/api/hooks/custom/index.js index fb774908a4..b4998f3a23 100644 --- a/website/api/hooks/custom/index.js +++ b/website/api/hooks/custom/index.js @@ -156,6 +156,9 @@ will be disabled and/or hidden in the UI. // FUTURE: Auto-redirect without the querystring after absorbtion to make it prettier in the URL bar. // (except this probably messes up analytics so before doing that, figure out how to solve that problem) }//fi + if(req.param('utm_content') === 'clear'){ + req.session.primaryBuyingSituation = undefined; + } if (req.method === 'GET' || req.method === 'HEAD') { // Include information about the primary buying situation // If set in the session (e.g. from an ad), use the primary buying situation for personalization. @@ -279,7 +282,6 @@ will be disabled and/or hidden in the UI. // Include information about the primary buying situation // If set in the session (e.g. from an ad), use the primary buying situation for personalization. res.locals.primaryBuyingSituation = req.session.primaryBuyingSituation || undefined; - }//fi return next(); diff --git a/website/assets/js/pages/pricing.page.js b/website/assets/js/pages/pricing.page.js index 6c2c678a17..69b6ba3e74 100644 --- a/website/assets/js/pages/pricing.page.js +++ b/website/assets/js/pages/pricing.page.js @@ -12,7 +12,13 @@ parasails.registerPage('pricing', { // β•‘ β•‘β• β•£ β•‘β•£ β•‘ β•šβ•¦β•β•‘ β•‘ β•‘β•£ // β•©β•β•β•©β•š β•šβ•β•β•šβ•β• β•© β•šβ•β•β•©β•β•β•šβ•β• beforeMount: function() { - //… + if(this.primaryBuyingSituation){ + if(['eo-security', 'vm'].includes(this.primaryBuyingSituation)){ + this.pricingMode = 'Security'; + } else { + this.pricingMode = 'IT'; + } + } }, mounted: async function(){ // Tooltips for desktop users are opened by a user hovering their cursor over them. diff --git a/website/assets/styles/pages/support.less b/website/assets/styles/pages/support.less index 37787cf9d5..5664f561a0 100644 --- a/website/assets/styles/pages/support.less +++ b/website/assets/styles/pages/support.less @@ -73,6 +73,7 @@ [purpose='community-cards'] { + margin-bottom: 30px; [purpose='support-card'] { width: 100%; height: 200px; @@ -91,7 +92,7 @@ width: 50%; } - margin-bottom: 30px; + } @media (min-width: 1201px) { @@ -104,7 +105,6 @@ [purpose='support-cards'] { flex-direction: row; - margin-bottom: 50px; max-width: 1120px; [purpose='support-row'] { flex-direction: row; @@ -119,6 +119,7 @@ } [purpose='community-cards'] { + margin-bottom: 50px; [purpose='support-card'] { width: 384px; height: 178px; @@ -151,9 +152,6 @@ } } @media (max-width: 991px) { - [purpose='support-card'] { - margin-bottom: 30px; - } [purpose='support-cards'] { [purpose='support-card'] { height: 221px; diff --git a/website/config/routes.js b/website/config/routes.js index 12e13b3a7d..6143564328 100644 --- a/website/config/routes.js +++ b/website/config/routes.js @@ -238,7 +238,7 @@ module.exports.routes = { action: 'view-device-management', locals: { pageTitleForMeta: 'Device management (MDM) | Fleet', - pageDescriptionForMeta: 'Configure your devices with sensible defaults, or customize MDM features exactly how you want. Manage your IT infrastructure in any browser or use git to make changes as code.', + pageDescriptionForMeta: 'Manage your devices in any browser or use git to make changes as code.', currentSection: 'platform', } }, @@ -247,7 +247,7 @@ module.exports.routes = { action: 'view-endpoint-ops', locals: { pageTitleForMeta: 'Endpoint ops | Fleet', - pageDescriptionForMeta: 'Simplify your security tooling, ship data to any platform, and pulse check anything with Fleet.', + pageDescriptionForMeta: 'Pulse check anything, build reports, and ship data to any platform with Fleet.', } }, @@ -255,7 +255,7 @@ module.exports.routes = { action: 'view-vulnerability-management', locals: { pageTitleForMeta: 'Vulnerability management | Fleet', - pageDescriptionForMeta: 'Instant, lightweight visibility down to the chipset of any endpoint. Consolidate your security stack and build the vulnerability program you actually want with Fleet.', + pageDescriptionForMeta: 'Report CVEs, software inventory, security posture, and other risks down to the chipset of any endpoint with Fleet.', } }, @@ -263,7 +263,7 @@ module.exports.routes = { action: 'view-support', locals: { pageTitleForMeta: 'Support | Fleet', - pageDescriptionForMeta: 'Ask a question, chat with other engineers, or get in touch with the Fleet team.', + pageDescriptionForMeta: 'Ask a question, chat with engineers, or get in touch with the Fleet team.', currentSection: 'documentation', } }, @@ -283,7 +283,7 @@ module.exports.routes = { hideFooterLinks: true, hideGetStartedButton: true, pageTitleForMeta: 'Start | Fleet', - pageDescriptionForMeta: 'Get Started with Fleet. Spin up a local demo or get your premium license key.', + pageDescriptionForMeta: 'Get Started with Fleet. Spin up a local demo or get your Premium license key.', } }, diff --git a/website/views/pages/contact.ejs b/website/views/pages/contact.ejs index 0982fc06eb..d446ef3e12 100644 --- a/website/views/pages/contact.ejs +++ b/website/views/pages/contact.ejs @@ -79,8 +79,8 @@
@@ -90,7 +90,11 @@
-

Includes computers, servers, containers, and other hosts.

+

Includes computers, servers, containers, and other hosts.

+

Includes servers, containers, workstations, and other hosts.

+

Includes macOS, Windows, Linux workstations, Chromebooks, servers, and other hosts.

+

Includes computers, servers, OT/ICS, containers, and other hosts.

+

Includes macOS, Windows, and most flavors of Linux.

Please enter a number of <%= primaryBuyingSituation === 'mdm' ? 'devices' : 'hosts' %>
diff --git a/website/views/pages/device-management.ejs b/website/views/pages/device-management.ejs index ce16776c7c..b0529e7bb1 100644 --- a/website/views/pages/device-management.ejs +++ b/website/views/pages/device-management.ejs @@ -195,7 +195,7 @@

Who else uses Fleet?

-

Empowering security and IT teams, globally

+

Empowering <%= ['vm', 'eo-security'].includes(primaryBuyingSituation) ? 'security and IT' : 'IT' %> teams, globally

diff --git a/website/views/pages/endpoint-ops.ejs b/website/views/pages/endpoint-ops.ejs index 6afe981f52..875048fb54 100644 --- a/website/views/pages/endpoint-ops.ejs +++ b/website/views/pages/endpoint-ops.ejs @@ -192,7 +192,7 @@

Who else uses Fleet?

-

Empowering security and IT teams, globally

+

Empowering <%= ['mdm', 'eo-it'].includes(primaryBuyingSituation) ? 'IT and security' : 'security and IT' %> teams, globally

diff --git a/website/views/pages/query-library.ejs b/website/views/pages/query-library.ejs index 11af02cb80..59172d0798 100644 --- a/website/views/pages/query-library.ejs +++ b/website/views/pages/query-library.ejs @@ -2,7 +2,10 @@
-

Device health checks

+

+ <%= ['eo-it', 'mdm'].includes(primaryBuyingSituation) ? 'Device health checks' : 'Built-in queries' %> + +

Fleet's standard query library includes a growing collection of useful queries for organizations deploying Fleet and osquery. Want to add your own query? Please contribute over on GitHub.

diff --git a/website/views/pages/support.ejs b/website/views/pages/support.ejs index c50168f4c9..8a1d76702e 100644 --- a/website/views/pages/support.ejs +++ b/website/views/pages/support.ejs @@ -1,12 +1,50 @@
+ +
+

Ask the community

+

Fleet has an active open-source community of kind and helpful people. If you have a question about something that isn't in the documentation, we hang out in:

+ <%primaryBuyingSituation%> +
+

Support

-

Ask a question, chat with other engineers, or get in touch with the Fleet team.

+

Ask a question, chat with engineers, or get in touch with the Fleet team.

+ -
-

Ask the community

-

Fleet has an active open-source community of knowledgeable and helpful users. If you have a question about something that isn't in the documentation, try one of these places:

-
- +
<%- /* Expose server-rendered data as window.SAILS_LOCALS :: */ exposeLocalsToBrowser() %> diff --git a/website/views/pages/vulnerability-management.ejs b/website/views/pages/vulnerability-management.ejs index 886e50a9e5..b60dee57ec 100644 --- a/website/views/pages/vulnerability-management.ejs +++ b/website/views/pages/vulnerability-management.ejs @@ -131,7 +131,7 @@

Who else uses Fleet?

-

Empowering security and IT teams, globally

+

Empowering <%= ['mdm', 'eo-it'].includes(primaryBuyingSituation) ? 'security and IT' : 'security' %> teams, globally

From 55df14a23e5ad194479206af2b4412dac0601ffa Mon Sep 17 00:00:00 2001 From: Rachael Shaw Date: Fri, 5 Apr 2024 11:11:18 -0500 Subject: [PATCH 44/60] Update schema overrides to clarify which tables require joining against users (#18045) For #16784. These tables require joining against `users`: + `chrome_extension_content_scripts` + `chrome_extensions` + `firefox_addons` + `vscode_extensions` + `browser_plugins` + `crashes` + `preferences` + `safari_extensions` + `ssh_configs` + `user_ssh_keys` + `authorized_keys` + `known_hosts` + `shell_history` --------- Co-authored-by: Eric --- schema/tables/authorized_keys.yml | 10 ++++------ schema/tables/browser_plugins.yml | 4 +++- schema/tables/chrome_extension_content_scripts.yml | 8 +++++++- schema/tables/chrome_extensions.yml | 13 ++++++++++--- schema/tables/crashes.yml | 7 ++----- schema/tables/firefox_addons.yml | 8 +++++++- schema/tables/known_hosts.yml | 10 +++++++++- schema/tables/preferences.yml | 8 ++++---- schema/tables/safari_extensions.yml | 8 +++++++- schema/tables/shell_history.yml | 11 ++++++++++- schema/tables/ssh_configs.yml | 8 +++++++- schema/tables/user_ssh_keys.yml | 8 +++++++- schema/tables/vscode_extensions.yml | 9 ++++++++- 13 files changed, 85 insertions(+), 27 deletions(-) diff --git a/schema/tables/authorized_keys.yml b/schema/tables/authorized_keys.yml index 64b591dc7d..4155daf65b 100644 --- a/schema/tables/authorized_keys.yml +++ b/schema/tables/authorized_keys.yml @@ -1,15 +1,13 @@ name: authorized_keys examples: >- - List the SSH keys allowed to connect to this host. - ``` - - SELECT key FROM authorized_keys; - + + SELECT * FROM users CROSS JOIN authorized_keys USING (uid); + ``` columns: - name: pid_with_namespace platforms: - linux - name: uid - requires_user_context: true +notes: Querying this table requires joining against the `users` table. [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table) diff --git a/schema/tables/browser_plugins.yml b/schema/tables/browser_plugins.yml index 3f51309252..4af49baec6 100644 --- a/schema/tables/browser_plugins.yml +++ b/schema/tables/browser_plugins.yml @@ -7,6 +7,8 @@ examples: >- ``` - SELECT bp.name, bp.identifier, bp.version FROM browser_plugins bp JOIN users u on bp.uid = u.uid ; + SELECT * FROM users CROSS JOIN browser_plugins USING (uid); ``` + +notes: Querying this table requires joining against the `users` table. [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table) diff --git a/schema/tables/chrome_extension_content_scripts.yml b/schema/tables/chrome_extension_content_scripts.yml index 2bba8eec38..2a9d2bb0b4 100644 --- a/schema/tables/chrome_extension_content_scripts.yml +++ b/schema/tables/chrome_extension_content_scripts.yml @@ -1,4 +1,10 @@ name: chrome_extension_content_scripts columns: - name: uid - requires_user_context: true +examples: >- + ``` + + SELECT * FROM users CROSS JOIN chrome_extension_content_scripts USING (uid); + + ``` +notes: Querying this table requires joining against the `users` table. [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table) diff --git a/schema/tables/chrome_extensions.yml b/schema/tables/chrome_extensions.yml index a993273c25..8ce6fced65 100644 --- a/schema/tables/chrome_extensions.yml +++ b/schema/tables/chrome_extensions.yml @@ -6,6 +6,12 @@ platforms: - chrome description: Installed extensions (plugins) for [Chromium-based](https://en.wikipedia.org/wiki/Chromium_(web_browser)) browsers, including [Google Chrome](https://en.wikipedia.org/wiki/Google_Chrome), [Edge](https://en.wikipedia.org/wiki/Microsoft_Edge), [Brave](https://en.wikipedia.org/wiki/Brave_(web_browser)), [Opera](https://en.wikipedia.org/wiki/Opera_(web_browser)), and [Yandex](https://en.wikipedia.org/wiki/Yandex_Browser). examples: >- + ``` + + SELECT * FROM users CROSS JOIN chrome_extensions USING (uid); + + ``` + List Chrome extensions by user and profile which have full access to HTTPS browsing. @@ -14,9 +20,12 @@ examples: >- SELECT u.username, ce.name, ce.description, ce.version, ce.profile, ce.permissions FROM users u CROSS JOIN chrome_extensions ce USING (uid) WHERE ce.permissions LIKE '%%https://*/*%%'; ``` +notes: | + Querying this table requires joining against the `users` table. [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table) + + On ChromeOS, this table requires the [fleetd Chrome extension](https://fleetdm.com/docs/using-fleet/chromeos). columns: - name: uid - requires_user_context: true platforms: - darwin - windows @@ -106,5 +115,3 @@ columns: - darwin - windows - linux -notes: | - - On ChromeOS, this table requires the [fleetd Chrome extension](https://fleetdm.com/docs/using-fleet/chromeos). diff --git a/schema/tables/crashes.yml b/schema/tables/crashes.yml index b56dc19838..ad76946839 100644 --- a/schema/tables/crashes.yml +++ b/schema/tables/crashes.yml @@ -1,13 +1,10 @@ name: crashes examples: >- - See software responsible for crashes. This can be useful to detect what the - most problematic software in your environment is. - ``` - SELECT crash_path, identifier, responsible, exception_type FROM crashes; + SELECT * FROM users CROSS JOIN crashes USING (uid); ``` +notes: Querying this table requires joining against the `users` table. [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table) columns: - name: uid - requires_user_context: true diff --git a/schema/tables/firefox_addons.yml b/schema/tables/firefox_addons.yml index 7c39cbe802..51eaf56f16 100644 --- a/schema/tables/firefox_addons.yml +++ b/schema/tables/firefox_addons.yml @@ -1,6 +1,12 @@ name: firefox_addons description: Firefox browser [add-ons](https://addons.mozilla.org/en-US/firefox/) (plugins). examples: >- + ``` + + SELECT * FROM users CROSS JOIN firefox_addons USING (uid); + + ``` + See Firefox extensions by user as well as information about their creator and automatic update status. @@ -9,6 +15,6 @@ examples: >- SELECT u.username, f.identifier, f.creator, f.description, f.version, f.autoupdate FROM users u CROSS JOIN firefox_addons f USING (uid) WHERE f.active='1'; ``` +notes: Querying this table requires joining against the `users` table. [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table) columns: - name: uid - requires_user_context: true diff --git a/schema/tables/known_hosts.yml b/schema/tables/known_hosts.yml index dd35e02eba..2f5132eac7 100644 --- a/schema/tables/known_hosts.yml +++ b/schema/tables/known_hosts.yml @@ -1,4 +1,12 @@ name: known_hosts columns: - name: uid - requires_user_context: true +examples: >- + ``` + + SELECT * FROM users CROSS JOIN known_hosts USING (uid); + + ``` + +notes: >- + - Querying this table requires joining against the `users` table. [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table) diff --git a/schema/tables/preferences.yml b/schema/tables/preferences.yml index aa3c43a462..8ab8bbaf77 100644 --- a/schema/tables/preferences.yml +++ b/schema/tables/preferences.yml @@ -1,15 +1,15 @@ name: preferences examples: >- This table reads a huge amount of preferences, including on third-party apps. - This query will show how many users are enrolled to TouchID. - + ``` - SELECT * FROM preferences WHERE subkey='dailyEvents/2/enrolledUserCount'; + SELECT * FROM users CROSS JOIN preferences USING (username); ``` notes: >- + - Querying this table requires joining against the `users` table. [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table) + - The `value` column will be empty for keys that contain binary data. columns: - name: username - requires_user_context: true diff --git a/schema/tables/safari_extensions.yml b/schema/tables/safari_extensions.yml index 5f190a000b..e90e669487 100644 --- a/schema/tables/safari_extensions.yml +++ b/schema/tables/safari_extensions.yml @@ -2,6 +2,12 @@ name: safari_extensions description: Installed Safari browser extensions (plugins). columns: - name: uid - requires_user_context: true +examples: >- + ``` + + SELECT * FROM users CROSS JOIN safari_extensions USING (uid); + + ``` notes: >- + - Querying this table requires joining against the `users` table. [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table) - Includes installed extensions for all system users. diff --git a/schema/tables/shell_history.yml b/schema/tables/shell_history.yml index 6de9db2d5b..01e4b08289 100644 --- a/schema/tables/shell_history.yml +++ b/schema/tables/shell_history.yml @@ -1,5 +1,11 @@ name: shell_history examples: >- + ``` + + SELECT * FROM users CROSS JOIN shell_history USING (uid); + + ``` + See command line executions and related timestamps. Useful for threat hunting when a device is suspected of being compromised. @@ -10,4 +16,7 @@ examples: >- ``` columns: - name: uid - requires_user_context: true + + +notes: >- + - Querying this table requires joining against the `users` table. [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table) diff --git a/schema/tables/ssh_configs.yml b/schema/tables/ssh_configs.yml index c745f09975..eb82e7f1d8 100644 --- a/schema/tables/ssh_configs.yml +++ b/schema/tables/ssh_configs.yml @@ -1,5 +1,11 @@ name: ssh_configs examples: >- + ``` + + SELECT * FROM users CROSS JOIN ssh_configs USING (uid); + + ``` + Identify SSH clients configured to send their locales to the server. ``` @@ -9,4 +15,4 @@ examples: >- ``` columns: - name: uid - requires_user_context: true +notes: Querying this table requires joining against the `users` table. [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table) \ No newline at end of file diff --git a/schema/tables/user_ssh_keys.yml b/schema/tables/user_ssh_keys.yml index 636f275cea..a51e5d7854 100644 --- a/schema/tables/user_ssh_keys.yml +++ b/schema/tables/user_ssh_keys.yml @@ -1,5 +1,11 @@ name: user_ssh_keys examples: >- + ``` + + SELECT * FROM users CROSS JOIN user_ssh_keys USING (uid); + + ``` + Identify SSH keys stored in clear text in user directories ``` @@ -12,4 +18,4 @@ columns: platforms: - linux - name: uid - requires_user_context: true +notes: Querying this table requires joining against the `users` table. [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table) diff --git a/schema/tables/vscode_extensions.yml b/schema/tables/vscode_extensions.yml index a2d17b398e..e6692078c3 100644 --- a/schema/tables/vscode_extensions.yml +++ b/schema/tables/vscode_extensions.yml @@ -1,6 +1,13 @@ name: vscode_extensions description: Installed extensions for [Visual Studio (VS) Code](https://code.visualstudio.com/). examples: >- + ``` + + SELECT * FROM users CROSS JOIN vscode_extensions USING (uid); + + ``` + + List the name, publisher, and version of the Visual Studio (VS) Code extensions installed on hosts. ``` @@ -8,7 +15,7 @@ examples: >- SELECT extension.name, extension.publisher, extension.version FROM users JOIN vscode_extensions extension USING (uid); ``` -notes: Querying this table requires joining against the `users` table. +notes: Querying this table requires joining against the `users` table. [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table) columns: - name: name description: Extension Name From 6f78531280646fb1e58c9856bc2aed2ee05357ab Mon Sep 17 00:00:00 2001 From: George Karr Date: Fri, 5 Apr 2024 11:13:58 -0500 Subject: [PATCH 45/60] Small changes to release and adding start of README (#18012) --- tools/release/README.md | 71 ++++ tools/release/publish_release.sh | 542 +++++++++++++++++-------------- 2 files changed, 366 insertions(+), 247 deletions(-) create mode 100644 tools/release/README.md diff --git a/tools/release/README.md b/tools/release/README.md new file mode 100644 index 0000000000..08aed8a25d --- /dev/null +++ b/tools/release/README.md @@ -0,0 +1,71 @@ + +# Releasing Fleet + +## Setup + +This script release requires various secrets to utilize chat GPT for formatting +as well as posting to Slack channels automatically + +``` + OPEN_API_KEY Open API key used for fallback if not provided via -o or --open-api-key option + SLACK_GENERAL_TOKEN Slack token to publish via curl to #general + SLACK_HELP_INFRA_TOKEN Slack token to publish via curl to #help-infrastructure + SLACK_HELP_ENG_TOKEN Slack token to publish via curl to #help-engineering +``` + +This requires: + `jq` `gh` `git` `curl` `awk` `sed` `make` `ack` `grep` + +The script will check that each of these are installed and available before running + +## Before running the script + +Make sure all tickets are tagged with the correct milestone. + +I recommend filtering by both the milestone you expect and also double check `no milestone` to make sure you haven't missed anything + +For example no tickets still in Ready / In Progress should be in the milestone we are about to release. + +## Main Release (end of sprint) + +example +``` +# Build release candidate and changelogs and QA ticket +./tools/release/publish_release.sh -a +# Do QA until ready to release + +# QA is passed on all teams and ready for release + +# Tag main +./tools/release/publish_release.sh -ag +# Publish main +./tools/release/publish_release.sh -au +# Go update osquery-slack version +``` + +... +TODO example output +... + + +## Patch Release (end of week / critical) + +example +``` +# Build release candidate and changelogs and QA ticket +./tools/release/publish_release.sh +# Do QA until ready to release + +# QA is passed on all teams and ready for release + +# Tag patch +./tools/release/publish_release.sh -g +# Publish patch +./tools/release/publish_release.sh -u +# Go update osquery-slack version +``` + +... +TODO example output +... + diff --git a/tools/release/publish_release.sh b/tools/release/publish_release.sh index 78d67e3693..6cc615af17 100755 --- a/tools/release/publish_release.sh +++ b/tools/release/publish_release.sh @@ -64,6 +64,8 @@ # |__/ |__/|________/|________/|________/|__/ |__/ \______/ |________/|__/ |__/ # +failed=false + usage() { echo "Usage: $0 [options] (optional|start_version)" echo "" @@ -73,6 +75,7 @@ usage() { echo " -d, --dry_run Perform a trial run with no changes made" echo " -f, --force Skip all confirmations" echo " -h, --help Display this help message and exit" + echo " -g, --tag Run the tag step" echo " -m, --minor Increment to a minor version instead of patch (Required if including non-bugs" echo " -o, --open_api_key Set the Open API key for calling out to ChatGPT" echo " -p, --print If the release is already drafted then print out the helpful info" @@ -83,7 +86,7 @@ usage() { echo " -v, --target_version Set the target version for the release" echo "" echo "Environment Variables:" - echo " OPEN_API_KEY Open API key used for fallback if not provided via -o or --open-api-key option" + echo " OPEN_API_KEY Open API key used for api requests to chat GPT" echo " SLACK_GENERAL_TOKEN Slack token to publish via curl to #general" echo " SLACK_HELP_INFRA_TOKEN Slack token to publish via curl to #help-infrastructure" echo " SLACK_HELP_ENG_TOKEN Slack token to publish via curl to #help-engineering" @@ -179,90 +182,266 @@ validate_and_format_date() { echo "Validated and formatted date: $target_date" } +build_changelog() { + if [ "$dry_run" = "false" ]; then + make changelog + + git diff CHANGELOG.md | $GREP_CMD '^+' | sed 's/^+//g' | $GREP_CMD -v CHANGELOG.md > new_changelog + prompt=$'I am creating a changelog for an open source project from a list of commit messages. Please format it for me using the following rules:\n1. Correct spelling and punctuation.\n2. Sentence casing.\n3. Past tense.\n4. Each list item is designated with an asterisk.\n5. Output in markdown format.' + if [[ "$main_release" == "true" ]]; then + # Place to make a main targeted prompt + #prompt=$'I am creating a changelog for an open source project from a list of commit messages. Please format it for me using the following rules:\n1. Correct spelling and punctuation.\n2. Sentence casing.\n3. Past tense.\n4. Each list item is designated with an asterisk.\n5. Output in markdown format.' + fi + + content=$(cat new_changelog | sed -E ':a;N;$!ba;s/\r{0,1}\n/\\n/g') + question="${prompt}\n\n${content}" + + # API endpoint for ChatGPT + api_endpoint="https://api.openai.com/v1/chat/completions" + output="null" + + while [[ "$output" == "null" ]]; do + data_payload=$(jq -n \ + --arg prompt "$question" \ + --arg model "gpt-3.5-turbo" \ + '{model: $model, messages: [{"role": "user", "content": $prompt}]}') + + response=$(curl -s -X POST $api_endpoint \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $open_api_key" \ + --data "$data_payload") + + output=`echo $response | jq -r .choices[0].message.content` + echo "${output}" + done + + git checkout CHANGELOG.md + if [[ "$target_date" == "" ]]; then + tartget_date=`date +"%b %d, %Y"` + fi + echo "## Fleet $target_milestone ($tartget_date)" > temp_changelog + echo "" >> temp_changelog + echo "### Bug fixes" >> temp_changelog + echo "" >> temp_changelog + echo -e "${output}" >> temp_changelog + echo "" >> temp_changelog + cp CHANGELOG.md old_changelog + cat temp_changelog + echo + echo "About to write changelog" + if [ "$force" = "false" ]; then + read -r -p "Does the above changelog look good (edit temp_changelog now to make changes) (n exits)? [y/N] " response + case "$response" in + [yY][eE][sS]|[yY]) + echo + ;; + *) + exit 1 + ;; + esac + fi + cat temp_changelog > CHANGELOG.md + cat old_changelog >> CHANGELOG.md + rm -f old_changelog + cp CHANGELOG.md /tmp + else + echo "DRYRUN: Would have formatted changelog" + fi +} + +changelog_and_versions() { + branch_for_changelog=$1 + source_branch=$2 + + local_exists=`git branch | $GREP_CMD $branch_for_changelog` + if [ "$dry_run" = "false" ]; then + if [[ $local_exists != "" ]]; then + # Clear previous + git branch -D $branch_for_changelog + fi + git checkout -b $branch_for_changelog + cp /tmp/CHANGELOG.md . + git add CHANGELOG.md + escaped_start_version=$(echo "$start_milestone" | sed 's/\./\\./g') + version_files=`ack -l --ignore-file=is:CHANGELOG.md "$escaped_start_version"` + unameOut="$(uname -s)" + case "${unameOut}" in + Linux*) echo "$version_files" | xargs sed -i "s/$escaped_start_version/$target_milestone/g";; + Darwin*) echo "$version_files" | xargs sed -i '' "s/$escaped_start_version/$target_milestone/g";; + *) echo "unknown distro to parse version" + esac + git add terraform charts infrastructure tools + git commit -m "Adding changes for patch $target_milestone" + git push origin $branch_for_changelog -f + gh pr create -f -B $source_branch + else + echo "DRYRUN: Would have created Changelog / verison pr from $branch_for_changelog to $source_branch" + fi +} + +create_qa_issue() { + if [ "$dry_run" = "false" ]; then + # Check for QA issue + found=$(gh issue list --search "Release QA: $target_milestone in:title" --json number | jq length) + if [[ "$found" == "0" ]]; then + cat .github/ISSUE_TEMPLATE/release-qa.md | awk 'BEGIN {count=0} /^---$/ {count++} count==2 && /^---$/ {getline; count++} count > 2 {print}' > temp_qa_issue_file + gh issue create --title "Release QA: $target_milestone" -F temp_qa_issue_file \ + --assignee "georgekarrv" --assignee "xpkoala" --label ":release" --label "#g-mdm" --label "#g-endpoint-ops" + rm -f temp_qa_issue_file + fi + else + echo "DRYRUN: Would have searched for and created if not found QA release ticket" + fi +} + print_announce_info() { - qa_ticket=`gh issue list --search "Release QA: $target_milestone in:title" --json url | jq -r .[0].url` - docker_deploy=`gh run list --workflow goreleaser-snapshot-fleet.yaml --json event,url,headBranch --limit 100 | jq -r "[.[]|select(.headBranch==\"$target_patch_branch\")][0].url"` - echo - echo "For announcing in #help-engineering" - echo "====================================================" - echo "Release $target_milestone QA ticket and docker publish" - echo "QA ticket for Release $target_milestone " $qa_ticket - echo "Docker Deploy status " $docker_deploy - echo "List of tickets pulled into release https://github.com/fleetdm/fleet/milestone/$target_milestone_number" - echo - slack_hook_url=https://hooks.slack.com/services - app_id=T019PP37ALW - announce_text="Release $target_milestone QA ticket and docker publish\nQA ticket for Release $target_milestone $qa_ticket\nDocker Deploy status $docker_deploy\nList of tickets pulled into release https://github.com/fleetdm/fleet/milestone/$target_milestone_number" - curl -X POST -H 'Content-type: application/json' \ - --data "{\"text\":\"$announce_text\"}" \ - $slack_hook_url/$app_id/$SLACK_HELP_ENG_TOKEN + if [ "$dry_run" = "false" ]; then + qa_ticket=`gh issue list --search "Release QA: $target_milestone in:title" --json url | jq -r .[0].url` + docker_deploy=`gh run list --workflow goreleaser-snapshot-fleet.yaml --json event,url,headBranch --limit 100 | jq -r "[.[]|select(.headBranch==\"$target_patch_branch\")][0].url"` + echo + echo "For announcing in #help-engineering" + echo "====================================================" + echo "Release $target_milestone QA ticket and docker publish" + echo "QA ticket for Release $target_milestone " $qa_ticket + echo "Docker Deploy status " $docker_deploy + echo "List of tickets pulled into release https://github.com/fleetdm/fleet/milestone/$target_milestone_number" + echo + slack_hook_url=https://hooks.slack.com/services + app_id=T019PP37ALW + announce_text="Release $target_milestone QA ticket and docker publish\nQA ticket for Release $target_milestone $qa_ticket\nDocker Deploy status $docker_deploy\nList of tickets pulled into release https://github.com/fleetdm/fleet/milestone/$target_milestone_number" + curl -X POST -H 'Content-type: application/json' \ + --data "{\"text\":\"$announce_text\"}" \ + $slack_hook_url/$app_id/$SLACK_HELP_ENG_TOKEN + else + echo "DRYRUN: Would have printed announce in #help-engineering text w/ qa ticket, deploy to docker link, and milestone issue list link" + fi } update_release_notes() { - if [ ! -f temp_changelog ]; then - echo "cannot find changelog to populate release notes" - exit 1 - fi - cat temp_changelog | tail -n +3 > release_notes - echo "" >> release_notes - echo "### Upgrading" >> release_notes - echo "" >> release_notes - echo "Please visit our [update guide](https://fleetdm.com/docs/deploying/upgrading-fleet) for upgrade instructions." >> release_notes - echo "" >> release_notes - echo "### Documentation" >> release_notes - echo "" >> release_notes - echo "Documentation for Fleet is available at [fleetdm.com/docs](https://fleetdm.com/docs)." >> release_notes - echo "" >> release_notes - echo "### Binary Checksum" >> release_notes - echo "" >> release_notes - echo "**SHA256**" >> release_notes - echo "" >> release_notes - echo '```' >> release_notes - gh release download $next_tag -p checksums.txt --clobber - cat checksums.txt >> release_notes - echo '```' >> release_notes - - echo - echo "============== Release Notes ========================" - cat release_notes - echo "============== Release Notes ========================" - if [ "$dry_run" = "false" ]; then + if [ ! -f temp_changelog ]; then + echo "cannot find changelog to populate release notes" + exit 1 + fi + cat temp_changelog | tail -n +3 > release_notes + echo "" >> release_notes + echo "### Upgrading" >> release_notes + echo "" >> release_notes + echo "Please visit our [update guide](https://fleetdm.com/docs/deploying/upgrading-fleet) for upgrade instructions." >> release_notes + echo "" >> release_notes + echo "### Documentation" >> release_notes + echo "" >> release_notes + echo "Documentation for Fleet is available at [fleetdm.com/docs](https://fleetdm.com/docs)." >> release_notes + echo "" >> release_notes + echo "### Binary Checksum" >> release_notes + echo "" >> release_notes + echo "**SHA256**" >> release_notes + echo "" >> release_notes + echo '```' >> release_notes + gh release download $next_tag -p checksums.txt --clobber + cat checksums.txt >> release_notes + echo '```' >> release_notes + + echo + echo "============== Release Notes ========================" + cat release_notes + echo "============== Release Notes ========================" + gh release edit --draft -F release_notes $next_tag + else + echo "DRYRUN: Would have created release notes based on temp_changelog" fi } +tag() { + if [ "$dry_run" = "false" ]; then + current_branch=`git rev-parse --abbrev-ref HEAD` + if [[ "$main_release" == "true" && "$current_branch" != "main" ]]; then + echo "Can't tag main release if you aren't on 'main'" + exit 1 + fi + if [[ "$main_release" == "true" ]]; then + # in main + found_version=`cat CHANGELOG.md | $GREP_CMD $target_milestone` + if [[ "$found_version" == "" ]]; then + echo "Can't tag main if CHANGELOG pr has not been merged yet" + exit 1 + fi + else + # patch release + if [[ "$current_branch" != "$target_patch_branch" ]]; then + echo "Can't tag patch release if you aren't on '$target_patch_branch'" + exit 1 + fi + fi + + # Officially tag and push + git tag $next_tag + git push origin $next_tag + + # This lets us wait for github actions to trigger + # we are specifically waiting for goreleaser to start + # off the `tag` branch ie: fleet-v4.47.2 to watch until it completes + # The last step of goreleaser is the create the draft release for us to modify later + show_spinner 200 + else + echo "DRYRUN: Would have tagged and pushed $next_tag" + fi + + if [ "$dry_run" = "false" ]; then + releaser_out=`gh run list --workflow goreleaser-fleet.yaml --json databaseId,event,headBranch,url | jq "[.[]|select(.headBranch==\"$next_tag\")][0]"` + echo "Releaser running " `echo $releaser_out | jq -r ".url"` + + gh run watch `echo $releaser_out | jq -r ".databaseId"` + else + echo "DRYRUN: Would found goreleaser action and waited for it to complete" + fi + + # Update draft release notes w/ changelog / notes / checksums + update_release_notes +} + + publish() { - gh release edit --draft=false --latest $next_tag - gh workflow run dogfood-deploy.yml -f DOCKER_IMAGE=fleetdm/fleet:$next_ver - show_spinner 200 - echo "=========================================================================" - echo "Update osquery Slack Fleet channel topic to say the correct version $next_ver" - echo "=========================================================================" - dogfood_deploy=`gh run list --workflow=dogfood-deploy.yml --status in_progress -L 1 --json url | jq -r '.[] | .url'` - cd tools/fleetctl-npm && npm publish + if [ "$dry_run" = "false" ]; then + # TODO more checks to validate we are ready to publish + gh release edit --draft=false --latest $next_tag + gh workflow run dogfood-deploy.yml -f DOCKER_IMAGE=fleetdm/fleet:$next_ver + show_spinner 200 + echo "=========================================================================" + echo "Update osquery Slack Fleet channel topic to say the correct version $next_ver" + echo "=========================================================================" + dogfood_deploy=`gh run list --workflow=dogfood-deploy.yml --status in_progress -L 1 --json url | jq -r '.[] | .url'` + cd tools/fleetctl-npm && npm publish - issues=`gh issue list -m $target_milestone --json number | jq -r '.[] | .number'` - for iss in $issues; do - echo "Closing #$iss" - gh issue close $iss - done + issues=`gh issue list -m $target_milestone --json number | jq -r '.[] | .number'` + for iss in $issues; do + is_story=`gh issue view $iss --json labels | jq -r '.labels | .[] | .name' | grep story` + # close all non-stories + if [[ "$is_story" == "" ]]; then + echo "Closing #$iss" + gh issue close $iss + fi + done - echo "Closing milestone" - gh api repos/fleetdm/fleet/milestones/$target_milestone_number -f state=closed + echo "Closing milestone" + gh api repos/fleetdm/fleet/milestones/$target_milestone_number -f state=closed - # Slack - slack_hook_url=https://hooks.slack.com/services - app_id=T019PP37ALW - announce_text=":cloud: :rocket: The latest version of Fleet is $target_milestone.\nMore info: https://github.com/fleetdm/fleet/releases/tag/$next_tag\nUpgrade now: https://fleetdm.com/docs/deploying/upgrading-fleet" + # Slack + slack_hook_url=https://hooks.slack.com/services + app_id=T019PP37ALW + announce_text=":cloud: :rocket: The latest version of Fleet is $target_milestone.\nMore info: https://github.com/fleetdm/fleet/releases/tag/$next_tag\nUpgrade now: https://fleetdm.com/docs/deploying/upgrading-fleet" - curl -X POST -H 'Content-type: application/json' \ - --data "{\"text\":\"$announce_text\"}" \ - $slack_hook_url/$app_id/$SLACK_GENERAL_TOKEN + curl -X POST -H 'Content-type: application/json' \ + --data "{\"text\":\"$announce_text\"}" \ + $slack_hook_url/$app_id/$SLACK_GENERAL_TOKEN - curl -X POST -H 'Content-type: application/json' \ - --data "{\"text\":\"$announce_text\nDogfood Deployed $dogfood_deploy\"}" \ - $slack_hook_url/$app_id/$SLACK_HELP_INFRA_TOKEN + curl -X POST -H 'Content-type: application/json' \ + --data "{\"text\":\"$announce_text\nDogfood Deployed $dogfood_deploy\"}" \ + $slack_hook_url/$app_id/$SLACK_HELP_INFRA_TOKEN + else + echo "DRYRUN: Would have published $next_tag / deployed to dogfood / closed non-stories / closed milestone / announced in slack" + fi } # Validate we have all commands required to perform this script @@ -280,6 +459,7 @@ target_version="" print_info=false publish_release=false release_notes=false +do_tag=false main_release=false # Parse long options manually @@ -297,6 +477,7 @@ for arg in "$@"; do "--publish_release") set -- "$@" "-u" ;; "--release_notes") set -- "$@" "-r" ;; "--start_version") set -- "$@" "-s" ;; + "--tag") set -- "$@" "-g" ;; "--target_date") set -- "$@" "-t" ;; "--target_version") set -- "$@" "-v" ;; *) set -- "$@" "$arg" @@ -304,13 +485,14 @@ for arg in "$@"; do done # Extract options and their arguments using getopts -while getopts "acdfhmo:prs:t:uv:" opt; do +while getopts "acdfhgmo:prs:t:uv:" opt; do case "$opt" in a) main_release=true ;; c) cherry_pick_resolved=true ;; d) dry_run=true ;; f) force=true ;; h) usage; exit 0 ;; + g) do_tag=true ;; m) minor=true ;; o) open_api_key=$OPTARG ;; p) print_info=true ;; @@ -357,15 +539,15 @@ if [ -z "$open_api_key" ]; then fi fi -if [ -n "$SLACK_GENERAL_TOKEN" ]; then +if [ -z "$SLACK_GENERAL_TOKEN" ]; then echo "Error: No SLACK_GENERAL_TOKEN environment variable." >&2 exit 1 fi -if [ -n "$SLACK_HELP_INFRA_TOKEN" ]; then +if [ -z "$SLACK_HELP_INFRA_TOKEN" ]; then echo "Error: No SLACK_HELP_INFRA_TOKEN environment variable." >&2 exit 1 fi -if [ -n "$SLACK_HELP_ENG_TOKEN" ]; then +if [ -z "$SLACK_HELP_ENG_TOKEN" ]; then echo "Error: No SLACK_HELP_ENG_TOKEN environment variable." >&2 exit 1 fi @@ -390,6 +572,11 @@ if [ -z "$start_version" ]; then fi fi +if [[ "$main_release" == "true" ]]; then + # Main releases are always minor releases + minor=true +fi + if [[ $start_version != v* ]]; then start_version=`echo "v$start_version"` fi @@ -442,43 +629,44 @@ fi # fleet-v4.48.0 next_tag="fleet-$next_ver" +if [[ "$target_milestone_number" == "" ]]; then + echo "Missing milestone $target_milestone, Please create one and tie tickets to the milestone to continue" + exit 1 +fi +echo "Found milestone $target_milestone with number $target_milestone_number" + + if [ "$print_info" = "true" ]; then print_announce_info exit 0 fi +if [ "$do_tag" = "true" ]; then + tag + exit 0 +fi + if [ "$release_notes" = "true" ]; then update_release_notes exit 0 fi -if [[ "$target_milestone_number" == "" ]]; then - echo "Missing milestone $target_milestone, Please create one and tie tickets to the milestone to continue" - exit 1 -fi -echo "Found milestone $target_milestone with number $target_milestone_number" - if [ "$publish_release" = "true" ]; then publish exit 0 fi -failed=false if [ "$cherry_pick_resolved" = "false" ]; then - if [ "$dry_run" = "false" ]; then - git fetch - fi - # TODO Fail if not found if [ "$dry_run" = "false" ]; then + git fetch git checkout $start_ver_tag git pull origin $start_ver_tag else echo "DRYRUN: Would have checked out starting tag $start_ver_tag" fi - local_exists=`git branch | $GREP_CMD $target_patch_branch` if [ "$dry_run" = "false" ]; then @@ -491,7 +679,6 @@ if [ "$cherry_pick_resolved" = "false" ]; then echo "DRYRUN: Would have cleared / checked out new branch $target_patch_branch" fi - total_prs=() issue_list=`gh issue list --search 'milestone:"'"$target_milestone"'"' --json number | jq -r '.[] | .number'` @@ -519,7 +706,6 @@ if [ "$cherry_pick_resolved" = "false" ]; then read -r -p "Check any issues that have no pull requests, no to cancel and yes to continue? [y/N] " response case "$response" in [yY][eE][sS]|[yY]) - echo "Continuing to cherry-pick" echo ;; *) @@ -531,6 +717,7 @@ if [ "$cherry_pick_resolved" = "false" ]; then commits="" if [[ "$main_release" == "false" ]]; then + echo "Continuing to cherry-pick" for pr in ${total_prs[*]}; do output=`gh pr view $pr --json state,mergeCommit,baseRefName` @@ -596,185 +783,46 @@ if [ "$cherry_pick_resolved" = "false" ]; then fi if [[ "$failed" == "false" ]]; then - if [ "$dry_run" = "false" ]; then - make changelog - git diff CHANGELOG.md | $GREP_CMD '^+' | sed 's/^+//g' | $GREP_CMD -v CHANGELOG.md > new_changelog - prompt=$'I am creating a changelog for an open source project from a list of commit messages. Please format it for me using the following rules:\n1. Correct spelling and punctuation.\n2. Sentence casing.\n3. Past tense.\n4. Each list item is designated with an asterisk.\n5. Output in markdown format.' - content=$(cat new_changelog | sed -E ':a;N;$!ba;s/\r{0,1}\n/\\n/g') - question="${prompt}\n\n${content}" + # have to push so we can make the PR's back + git push origin $target_patch_branch -f + fi - # API endpoint for ChatGPT - api_endpoint="https://api.openai.com/v1/chat/completions" - output="null" + build_changelog - while [[ "$output" == "null" ]]; do - data_payload=$(jq -n \ - --arg prompt "$question" \ - --arg model "gpt-3.5-turbo" \ - '{model: $model, messages: [{"role": "user", "content": $prompt}]}') - - response=$(curl -s -X POST $api_endpoint \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer $open_api_key" \ - --data "$data_payload") - - output=`echo $response | jq -r .choices[0].message.content` - echo "${output}" - done - else - echo "DRYRUN: Would have run make changelog and sent to ChatGPT to format" + if [[ "$main_release" == "false" ]]; then + # Create PR for changelog and version to patch + update_changelog_patch_branch="update-changelog-pb-$target_milestone" + changelog_and_versions $update_changelog_patch_branch $target_patch_branch fi if [ "$dry_run" = "false" ]; then - git checkout CHANGELOG.md - if [[ "$target_date" == "" ]]; then - tartget_date=`date +"%b %d, %Y"` - fi - echo "## Fleet $target_milestone ($tartget_date)" > temp_changelog - echo "" >> temp_changelog - echo "### Bug fixes" >> temp_changelog - echo "" >> temp_changelog - echo -e "${output}" >> temp_changelog - echo "" >> temp_changelog - cp CHANGELOG.md old_changelog - cat temp_changelog - echo - echo "About to write changelog" - if [ "$force" = "false" ]; then - read -r -p "Does the above changelog look good (edit temp_changelog now to make changes) (n exits)? [y/N] " response - case "$response" in - [yY][eE][sS]|[yY]) - echo - ;; - *) - exit 1 - ;; - esac - fi - cat temp_changelog > CHANGELOG.md - cat old_changelog >> CHANGELOG.md - rm -f old_changelog - update_changelog_patch_branch="update-changelog-pb-$target_milestone" - local_exists=`git branch | $GREP_CMD $update_changelog_patch_branch` - if [[ $local_exists != "" ]]; then - # Clear previous - git branch -D $update_changelog_patch_branch - fi - git checkout -b $update_changelog_patch_branch - git add CHANGELOG.md - escaped_start_version=$(echo "$start_milestone" | sed 's/\./\\./g') - version_files=`ack -l --ignore-file=is:CHANGELOG.md "$escaped_start_version"` - unameOut="$(uname -s)" - case "${unameOut}" in - Linux*) echo "$version_files" | xargs sed -i "s/$escaped_start_version/$target_milestone/g";; - Darwin*) echo "$version_files" | xargs sed -i '' "s/$escaped_start_version/$target_milestone/g";; - *) echo "unknown distro to parse version" - esac - git add terraform charts infrastructure tools - git commit -m "Adding changes for patch $target_milestone" - git push origin $update_changelog_patch_branch -f - gh pr create -f -B $target_patch_branch - - cp CHANGELOG.md /tmp + # Create PR for changelog and version to main git checkout main git pull origin main - update_changelog_branch="update-changelog-$target_milestone" - local_exists=`git branch | $GREP_CMD $update_changelog_branch` - if [[ $local_exists != "" ]]; then - # Clear previous - git branch -D $update_changelog_branch - fi - git checkout -b $update_changelog_branch - cp /tmp/CHANGELOG.md . - git add CHANGELOG.md - escaped_start_version=$(echo "$start_milestone" | sed 's/\./\\./g') - version_files=`ack -l --ignore-file=is:CHANGELOG.md "$escaped_start_version"` - unameOut="$(uname -s)" - case "${unameOut}" in - Linux*) echo "$version_files" | xargs sed -i "s/$escaped_start_version/$target_milestone/g";; - Darwin*) echo "$version_files" | xargs sed -i '' "s/$escaped_start_version/$target_milestone/g";; - *) echo "unknown distro to parse version" - esac - git add terraform charts infrastructure tools - git commit -m "Updating changelog for $target_milestone" - git push origin $update_changelog_branch -f - gh pr create -f + else + echo "DRYRUN: Would have switched to main and pulled latest" + fi + update_changelog_branch="update-changelog-$target_milestone" + changelog_and_versions $update_changelog_branch main + if [ "$dry_run" = "false" ]; then + # Back on patch / prepare git checkout $target_patch_branch else - echo "DRYRUN: Would have formatted changelog and created PR on main" + echo "DRYRUN: Would have switched back to branch $target_patch_branch" fi # Check for QA issue - if [ "$dry_run" = "false" ]; then - found=$(gh issue list --search "Release QA: $target_milestone in:title" --json number | jq length) - if [[ "$found" == "0" ]]; then - cat .github/ISSUE_TEMPLATE/release-qa.md | awk 'BEGIN {count=0} /^---$/ {count++} count==2 && /^---$/ {getline; count++} count > 2 {print}' > temp_qa_issue_file - gh issue create --title "Release QA: $target_milestone" -F temp_qa_issue_file \ - --assignee "sabrinabuckets" --assignee "xpkoala" --label ":release" --label "#g-mdm" --label "#g-endpoint-ops" - rm -f temp_qa_issue_file - fi - else - echo "DRYRUN: Would have searched for and created if not found QA release ticket" - fi + create_qa_issue if [ "$dry_run" = "false" ]; then echo "Waiting for github actions to propogate..." show_spinner 200 - # For announce in #help-engineering - print_announce_info - else - echo "DRYRUN: Would have printed announce in #help-engineering text w/ qa ticket, deploy to docker link, and milestone issue list link" fi - if [ "$dry_run" = "false" ]; then - echo "waiting for Changelog PR to merge..." - echo `gh pr view $update_changelog_patch_branch --json url | jq -r .url` - echo - waiting=true - while $waiting; do - pr_state=`gh pr view $update_changelog_patch_branch --json state | jq -r .state` - if [[ "$pr_state" == "MERGED" ]]; then - waiting=false - else - show_spinner 50 - fi - done - git pull origin $target_patch_branch - - - echo "About to tag to $next_tag" - if [ "$force" = "false" ]; then - read -r -p "Did all steps succeed and is the tag ready to push? [y/N] " response - case "$response" in - [yY][eE][sS]|[yY]) - echo - ;; - *) - exit 1 - ;; - esac - fi - git tag $next_tag - git push origin $next_tag - - show_spinner 200 - else - echo "DRYRUN: Would have tagged and pushed $next_tag" - fi - - if [ "$dry_run" = "false" ]; then - releaser_out=`gh run list --workflow goreleaser-fleet.yaml --json databaseID,event,headBranch,url | jq "[.[]|select(.headBranch==\"$next_tag\")[0]` - echo "Releaser running " `echo $releaser_out | jq -r ".url"` - - gh run watch `echo $releaser_out | jq -r ".databaseID"` - else - echo "DRYRUN: Would found goreleaser action and waited for it to complete" - fi - - - update_release_notes + # For announce in #help-engineering + print_announce_info else # TODO echo what to do echo "Placeholder, Cherry pick failed....figure out what to do..." From 5a6c407e1bb00dc579b6101485739efc574de4fa Mon Sep 17 00:00:00 2001 From: Victor Lyuboslavsky Date: Fri, 5 Apr 2024 12:44:56 -0500 Subject: [PATCH 46/60] Calendar webhook will retry if it receives response 429 (#18067) #18044 Calendar webhook will retry if it receives response 429 Too Many Requests. Webhook request will retry for 30 minutes with a 1 minute max delay between retries. # Checklist for submitter If some of the following don't apply, delete the relevant line. - [x] Changes file added for user-visible changes in `changes/` or `orbit/changes/`. See [Changes files](https://fleetdm.com/docs/contributing/committing-changes#changes-files) for more information. - [ ] Added/updated tests - [x] Manual QA for all new/changed functionality --- changes/18044-calendar-webhook-retry | 1 + server/cron/calendar_cron.go | 2 +- server/fleet/calendar_events.go | 1 + server/service/osquery.go | 37 ++++- tools/calendar/delete-events/delete-events.go | 147 +++++++++++++----- tools/calendar/move-events/move-events.go | 75 +++++++-- 6 files changed, 206 insertions(+), 57 deletions(-) create mode 100644 changes/18044-calendar-webhook-retry diff --git a/changes/18044-calendar-webhook-retry b/changes/18044-calendar-webhook-retry new file mode 100644 index 0000000000..d624068b02 --- /dev/null +++ b/changes/18044-calendar-webhook-retry @@ -0,0 +1 @@ +Calendar webhook will retry if it receives response 429 Too Many Requests. Webhook request will retry for 30 minutes with a 1 minute max delay between retries. diff --git a/server/cron/calendar_cron.go b/server/cron/calendar_cron.go index 3b9cd5b24f..adfd76509e 100644 --- a/server/cron/calendar_cron.go +++ b/server/cron/calendar_cron.go @@ -222,7 +222,7 @@ func processCalendarFailingHosts( // thus we skip this entry. continue // continue with next host } - if hostCalendarEvent.WebhookStatus == fleet.CalendarWebhookStatusPending { + if hostCalendarEvent.WebhookStatus == fleet.CalendarWebhookStatusPending || hostCalendarEvent.WebhookStatus == fleet.CalendarWebhookStatusRetry { // This can happen if the host went offline (and never returned results) // after setting the webhook as pending. continue // continue with next host diff --git a/server/fleet/calendar_events.go b/server/fleet/calendar_events.go index 4574d23725..d7b22d478e 100644 --- a/server/fleet/calendar_events.go +++ b/server/fleet/calendar_events.go @@ -19,6 +19,7 @@ const ( CalendarWebhookStatusPending CalendarWebhookStatusSent CalendarWebhookStatusError + CalendarWebhookStatusRetry ) type HostCalendarEvent struct { diff --git a/server/service/osquery.go b/server/service/osquery.go index 31e4e33d27..b007c279f7 100644 --- a/server/service/osquery.go +++ b/server/service/osquery.go @@ -12,6 +12,7 @@ import ( "sync/atomic" "time" + "github.com/cenkalti/backoff/v4" "github.com/fleetdm/fleet/v4/server" "github.com/fleetdm/fleet/v4/server/contexts/ctxerr" hostctx "github.com/fleetdm/fleet/v4/server/contexts/host" @@ -21,6 +22,7 @@ import ( "github.com/fleetdm/fleet/v4/server/ptr" "github.com/fleetdm/fleet/v4/server/pubsub" "github.com/fleetdm/fleet/v4/server/service/osquery_utils" + kithttp "github.com/go-kit/kit/transport/http" "github.com/go-kit/log" "github.com/go-kit/log/level" "github.com/spf13/cast" @@ -1166,15 +1168,36 @@ func processCalendarPolicies( } go func() { - if err := fleet.FireCalendarWebhook( - team.Config.Integrations.GoogleCalendar.WebhookURL, - host.ID, host.HardwareSerial, host.DisplayName(), failingCalendarPolicies, "", - ); err != nil { + retryStrategy := backoff.NewExponentialBackOff() + retryStrategy.MaxElapsedTime = 30 * time.Minute + err := backoff.Retry( + func() error { + if err := fleet.FireCalendarWebhook( + team.Config.Integrations.GoogleCalendar.WebhookURL, + host.ID, host.HardwareSerial, host.DisplayName(), failingCalendarPolicies, "", + ); err != nil { + var statusCoder kithttp.StatusCoder + if errors.As(err, &statusCoder) && statusCoder.StatusCode() == http.StatusTooManyRequests { + level.Debug(logger).Log("msg", "fire webhook", "err", err) + if err := ds.UpdateHostCalendarWebhookStatus( + context.Background(), host.ID, fleet.CalendarWebhookStatusRetry, + ); err != nil { + level.Error(logger).Log("msg", "mark fired webhook as retry", "err", err) + } + return err + } + return backoff.Permanent(err) + } + return nil + }, retryStrategy, + ) + nextStatus := fleet.CalendarWebhookStatusSent + if err != nil { level.Error(logger).Log("msg", "fire webhook", "err", err) - return + nextStatus = fleet.CalendarWebhookStatusError } - if err := ds.UpdateHostCalendarWebhookStatus(context.Background(), host.ID, fleet.CalendarWebhookStatusSent); err != nil { - level.Error(logger).Log("msg", "mark fired webhook as sent", "err", err) + if err := ds.UpdateHostCalendarWebhookStatus(context.Background(), host.ID, nextStatus); err != nil { + level.Error(logger).Log("msg", fmt.Sprintf("mark fired webhook as %v", nextStatus), "err", err) } }() diff --git a/tools/calendar/delete-events/delete-events.go b/tools/calendar/delete-events/delete-events.go index cfa0b2ce07..23933e11bf 100644 --- a/tools/calendar/delete-events/delete-events.go +++ b/tools/calendar/delete-events/delete-events.go @@ -2,16 +2,24 @@ package main import ( "context" + "errors" "flag" + "github.com/cenkalti/backoff/v4" "golang.org/x/oauth2/google" "golang.org/x/oauth2/jwt" "google.golang.org/api/calendar/v3" + "google.golang.org/api/googleapi" "google.golang.org/api/option" "log" + "net/http" "os" + "strings" + "sync" + "time" ) -// Delete all events with eventTitle from the primary calendar of the user. +// Delete all events with eventTitle from the primary calendar of the specified users. +// Example: go run delete-events.go --users john@example.com,jane@example.com var ( serviceEmail = os.Getenv("FLEET_TEST_GOOGLE_CALENDAR_SERVICE_EMAIL") @@ -26,49 +34,114 @@ func main() { if serviceEmail == "" || privateKey == "" { log.Fatal("FLEET_TEST_GOOGLE_CALENDAR_SERVICE_EMAIL and FLEET_TEST_GOOGLE_CALENDAR_PRIVATE_KEY must be set") } - userEmail := flag.String("user", "", "User email to impersonate") + userEmails := flag.String("users", "", "Comma-separated list of user emails to impersonate") flag.Parse() - if *userEmail == "" { - log.Fatal("--user is required") + if *userEmails == "" { + log.Fatal("--users are required") + } + userEmailList := strings.Split(*userEmails, ",") + if len(userEmailList) == 0 { + log.Fatal("No user emails provided") } ctx := context.Background() - conf := &jwt.Config{ - Email: serviceEmail, - Scopes: []string{ - "https://www.googleapis.com/auth/calendar.events", "https://www.googleapis.com/auth/calendar.settings.readonly", - }, - PrivateKey: []byte(privateKey), - TokenURL: google.JWTTokenURL, - Subject: *userEmail, - } - client := conf.Client(ctx) - // Create a new calendar service - service, err := calendar.NewService(ctx, option.WithHTTPClient(client)) - if err != nil { - log.Fatalf("Unable to create Calendar service: %v", err) - } - numberDeleted := 0 - for { - list, err := service.Events.List("primary").EventTypes("default").MaxResults(1000).OrderBy("startTime").SingleEvents(true).ShowDeleted(false).Q(eventTitle).Do() - if err != nil { - log.Fatalf("Unable to retrieve list of events: %v", err) - } - if len(list.Items) == 0 { - break - } - for _, item := range list.Items { - if item.Summary == eventTitle { - err = service.Events.Delete("primary", item.Id).Do() + + var wg sync.WaitGroup + + for _, userEmail := range userEmailList { + wg.Add(1) + go func(userEmail string) { + defer wg.Done() + conf := &jwt.Config{ + Email: serviceEmail, + Scopes: []string{ + "https://www.googleapis.com/auth/calendar.events", "https://www.googleapis.com/auth/calendar.settings.readonly", + }, + PrivateKey: []byte(privateKey), + TokenURL: google.JWTTokenURL, + Subject: userEmail, + } + client := conf.Client(ctx) + // Create a new calendar service + service, err := calendar.NewService(ctx, option.WithHTTPClient(client)) + if err != nil { + log.Fatalf("Unable to create Calendar service: %v", err) + } + numberDeleted := 0 + for { + list, err := withRetry( + func() (any, error) { + return service.Events.List("primary"). + EventTypes("default"). + MaxResults(1000). + OrderBy("startTime"). + SingleEvents(true). + ShowDeleted(false). + Q(eventTitle). + Do() + }, + ) if err != nil { - log.Fatalf("Unable to delete event: %v", err) + log.Fatalf("Unable to retrieve list of events: %v", err) } - numberDeleted++ - if numberDeleted%10 == 0 { - log.Printf("Deleted %d events", numberDeleted) + if len(list.(*calendar.Events).Items) == 0 { + break + } + for _, item := range list.(*calendar.Events).Items { + if item.Summary == eventTitle { + _, err := withRetry( + func() (any, error) { + return nil, service.Events.Delete("primary", item.Id).Do() + }, + ) + if err != nil { + log.Fatalf("Unable to delete event: %v", err) + } + numberDeleted++ + if numberDeleted%10 == 0 { + log.Printf("Deleted %d events for %s", numberDeleted, userEmail) + } + } } } - } + log.Printf("DONE. Deleted %d events total for %s", numberDeleted, userEmail) + }(userEmail) } - log.Printf("DONE. Deleted %d events total", numberDeleted) + + // Wait for all goroutines to finish + wg.Wait() + +} + +func withRetry(fn func() (any, error)) (any, error) { + retryStrategy := backoff.NewExponentialBackOff() + retryStrategy.MaxElapsedTime = 60 * time.Minute + var result any + err := backoff.Retry( + func() error { + var err error + result, err = fn() + if err != nil { + if isRateLimited(err) { + return err + } + return backoff.Permanent(err) + } + return nil + }, retryStrategy, + ) + return result, err +} + +func isRateLimited(err error) bool { + if err == nil { + return false + } + var ae *googleapi.Error + ok := errors.As(err, &ae) + return ok && (ae.Code == http.StatusTooManyRequests || + (ae.Code == http.StatusForbidden && + (ae.Message == "Rate Limit Exceeded" || ae.Message == "User Rate Limit Exceeded" || ae.Message == "Calendar usage limits exceeded." || strings.HasPrefix( + ae.Message, "Quota exceeded", + )))) } diff --git a/tools/calendar/move-events/move-events.go b/tools/calendar/move-events/move-events.go index 2e66b56e64..d4906eaef1 100644 --- a/tools/calendar/move-events/move-events.go +++ b/tools/calendar/move-events/move-events.go @@ -2,12 +2,16 @@ package main import ( "context" + "errors" "flag" + "github.com/cenkalti/backoff/v4" "golang.org/x/oauth2/google" "golang.org/x/oauth2/jwt" "google.golang.org/api/calendar/v3" + "google.golang.org/api/googleapi" "google.golang.org/api/option" "log" + "net/http" "os" "strings" "sync" @@ -16,6 +20,7 @@ import ( // Move all events with eventTitle from the primary calendar of the user to the new time. // Only events in the future relative to the new event time are moved. In other words, if the current event time is in the past, it is not moved. +// Example: go run move-events.go --users john@example.com,jane@example.com --datetime 2024-04-01T10:00:00Z var ( serviceEmail = os.Getenv("FLEET_TEST_GOOGLE_CALENDAR_SERVICE_EMAIL") @@ -75,33 +80,46 @@ func main() { numberMoved := 0 for { - list, err := service.Events.List("primary").EventTypes("default"). - MaxResults(1000). - OrderBy("startTime"). - SingleEvents(true). - ShowDeleted(false). - TimeMin(dateTimeEndStr). - Q(eventTitle). - Do() + list, err := withRetry( + func() (any, error) { + return service.Events.List("primary").EventTypes("default"). + MaxResults(1000). + OrderBy("startTime"). + SingleEvents(true). + ShowDeleted(false). + TimeMin(dateTimeEndStr). + Q(eventTitle). + Do() + }, + ) + if err != nil { log.Fatalf("Unable to retrieve list of events: %v", err) } - if len(list.Items) == 0 { + if len(list.(*calendar.Events).Items) == 0 { break } - for _, item := range list.Items { + for _, item := range list.(*calendar.Events).Items { if item.Summary == eventTitle { item.Start.DateTime = dateTime.Format(time.RFC3339) item.End.DateTime = dateTime.Add(30 * time.Minute).Format(time.RFC3339) - _, err := service.Events.Update("primary", item.Id, item).Do() + _, err := withRetry( + func() (any, error) { + return service.Events.Update("primary", item.Id, item).Do() + }, + ) if err != nil { log.Fatalf("Unable to update event: %v", err) } numberMoved++ + if numberMoved%10 == 0 { + log.Printf("Moved %d events for %s", numberMoved, userEmail) + } + } } } - log.Printf("Moved %d events for %s", numberMoved, userEmail) + log.Printf("DONE. Moved total %d events for %s", numberMoved, userEmail) }(userEmail) } @@ -109,3 +127,36 @@ func main() { wg.Wait() } + +func withRetry(fn func() (any, error)) (any, error) { + retryStrategy := backoff.NewExponentialBackOff() + retryStrategy.MaxElapsedTime = 60 * time.Minute + var result any + err := backoff.Retry( + func() error { + var err error + result, err = fn() + if err != nil { + if isRateLimited(err) { + return err + } + return backoff.Permanent(err) + } + return nil + }, retryStrategy, + ) + return result, err +} + +func isRateLimited(err error) bool { + if err == nil { + return false + } + var ae *googleapi.Error + ok := errors.As(err, &ae) + return ok && (ae.Code == http.StatusTooManyRequests || + (ae.Code == http.StatusForbidden && + (ae.Message == "Rate Limit Exceeded" || ae.Message == "User Rate Limit Exceeded" || ae.Message == "Calendar usage limits exceeded." || strings.HasPrefix( + ae.Message, "Quota exceeded", + )))) +} From 9e2be0028c3f5717e7dfa0faa7053941e8e87ea3 Mon Sep 17 00:00:00 2001 From: Jacob Shandling <61553566+jacobshandling@users.noreply.github.com> Date: Fri, 5 Apr 2024 11:47:42 -0700 Subject: [PATCH 47/60] UI: Restore missing JavaScript context in HQR config (#18087) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Addresses #18083 ![Screenshot 2024-04-05 at 9 37 20β€―AM](https://github.com/fleetdm/fleet/assets/61553566/b683d30c-9af2-4cbb-8cea-b4a6e2422464) - [x] Changes file added for user-visible changes in `changes/` - [x] Added/updated tests - [x] Manual QA for all new/changed functionality --------- Co-authored-by: Jacob Shandling --- ...83-no-values-in-host-details-query-reports | 1 + .../HQRTable/HQRTable.tests.tsx | 112 ++++++++++++++++++ .../HostQueryReport/HQRTable/HQRTable.tsx | 2 +- .../HQRTable/HQRTableConfig.tsx | 8 +- 4 files changed, 116 insertions(+), 7 deletions(-) create mode 100644 changes/18083-no-values-in-host-details-query-reports create mode 100644 frontend/pages/hosts/details/HostQueryReport/HQRTable/HQRTable.tests.tsx diff --git a/changes/18083-no-values-in-host-details-query-reports b/changes/18083-no-values-in-host-details-query-reports new file mode 100644 index 0000000000..1c1a19a367 --- /dev/null +++ b/changes/18083-no-values-in-host-details-query-reports @@ -0,0 +1 @@ +- Fix a bug where values were not being rendered in host-specific query reports. diff --git a/frontend/pages/hosts/details/HostQueryReport/HQRTable/HQRTable.tests.tsx b/frontend/pages/hosts/details/HostQueryReport/HQRTable/HQRTable.tests.tsx new file mode 100644 index 0000000000..e040e521e1 --- /dev/null +++ b/frontend/pages/hosts/details/HostQueryReport/HQRTable/HQRTable.tests.tsx @@ -0,0 +1,112 @@ +import React from "react"; + +import { render, screen } from "@testing-library/react"; + +import HQRTable, { IHQRTable } from "./HQRTable"; + +describe("HQRTable component", () => { + it("Renders results normally when they are present", () => { + const testData: IHQRTable[] = [ + { + queryName: "testQuery0", + queryDescription: "testDescription0", + hostName: "testHost0", + rows: [ + { + build_distro: "10.14", + build_platform: "darwin", + config_hash: "111111111111111111111111", + config_valid: "1", + extensions: "active", + instance_id: "2f7a7b8e-8f35-4fa8-9e8b-1111111111111111", + pid: "575", + platform_mask: "21", + start_time: "1711512878", + uuid: "gggggg-4568-5BD9-9F1C-6D2E701FAB5C", + version: "5.11.0", + watcher: "574", + }, + ], + reportClipped: false, + lastFetched: "2021-09-01T00:00:00Z", + onShowQuery: jest.fn(), + isLoading: false, + }, + ]; + + testData.forEach((tableProps) => { + render(); + expect(screen.getByText("1 result")).toBeInTheDocument(); + expect(screen.getByText("Last fetched")).toBeInTheDocument(); + tableProps.rows.forEach((row) => { + Object.entries(row).forEach(([col, val]) => { + expect(screen.getByText(col)).toBeInTheDocument(); + expect(screen.getByText(val)).toBeInTheDocument(); + }); + }); + }); + }); + + it("Renders the 'collecting results' empty state when results have never been collected.", () => { + const testData: IHQRTable[] = [ + { + queryName: "testQuery0", + queryDescription: "testDescription0", + hostName: "testHost0", + rows: [], + reportClipped: false, + lastFetched: null, + onShowQuery: jest.fn(), + isLoading: false, + }, + ]; + + testData.forEach((tableProps) => { + render(); + expect(screen.queryByText("Last fetched")).toBeNull(); + expect(screen.getByText("Collecting results...")).toBeInTheDocument(); + }); + }); + + it("Renders the 'report clipped' empty state when reporting for this query has been paused and there are no existing results.", () => { + const testData: IHQRTable[] = [ + { + queryName: "testQuery0", + queryDescription: "testDescription0", + hostName: "testHost0", + rows: [], + reportClipped: true, + lastFetched: "2021-09-01T00:00:00Z", + onShowQuery: jest.fn(), + isLoading: false, + }, + ]; + + testData.forEach((tableProps) => { + render(); + expect(screen.queryByText("Last fetched")).toBeNull(); + expect(screen.getByText("Report clipped")).toBeInTheDocument(); + }); + }); + + it("Renders the 'nothing to report' empty state when the query has run and there are no results.", () => { + const testData: IHQRTable[] = [ + { + queryName: "testQuery0", + queryDescription: "testDescription0", + hostName: "testHost0", + rows: [], + reportClipped: false, + lastFetched: "2021-09-01T00:00:00Z", + onShowQuery: jest.fn(), + isLoading: false, + }, + ]; + + testData.forEach((tableProps) => { + render(); + expect(screen.queryByText("Last fetched")).toBeNull(); + expect(screen.getByText("Nothing to report")).toBeInTheDocument(); + }); + }); +}); diff --git a/frontend/pages/hosts/details/HostQueryReport/HQRTable/HQRTable.tsx b/frontend/pages/hosts/details/HostQueryReport/HQRTable/HQRTable.tsx index b536730514..30969112a1 100644 --- a/frontend/pages/hosts/details/HostQueryReport/HQRTable/HQRTable.tsx +++ b/frontend/pages/hosts/details/HostQueryReport/HQRTable/HQRTable.tsx @@ -15,7 +15,7 @@ import generateColumnConfigs from "./HQRTableConfig"; const baseClass = "hqr-table"; -interface IHQRTable { +export interface IHQRTable { queryName?: string; queryDescription?: string; hostName?: string; diff --git a/frontend/pages/hosts/details/HostQueryReport/HQRTable/HQRTableConfig.tsx b/frontend/pages/hosts/details/HostQueryReport/HQRTable/HQRTableConfig.tsx index c220358b10..d5252ec5df 100644 --- a/frontend/pages/hosts/details/HostQueryReport/HQRTable/HQRTableConfig.tsx +++ b/frontend/pages/hosts/details/HostQueryReport/HQRTable/HQRTableConfig.tsx @@ -1,10 +1,6 @@ import DefaultColumnFilter from "components/TableContainer/DataTable/DefaultColumnFilter"; import HeaderCell from "components/TableContainer/DataTable/HeaderCell"; -import { - IHeaderProps, - IStringCellProps, - IWebSocketData, -} from "interfaces/datatable_config"; +import { IHeaderProps, IWebSocketData } from "interfaces/datatable_config"; import React from "react"; import { CellProps, Column } from "react-table"; @@ -47,7 +43,7 @@ const generateColumnConfigs = (rows: IWebSocketData[]): IHQRTTableColumn[] => const val = cellProps?.cell?.value; return !!val?.length && val.length > 300 ? internallyTruncateText(val) - : <>val ?? null; + : <>{val} ?? null; }, Filter: DefaultColumnFilter, // Component hides filter for last_fetched filterType: "text", From 7bf3157fef038960704e24b236cd2d1ab54eb404 Mon Sep 17 00:00:00 2001 From: Rachael Shaw Date: Fri, 5 Apr 2024 14:00:04 -0500 Subject: [PATCH 48/60] Add redirects for calendar integration setup (#18094) Adds redirects used in the Fleet UI for [calendar integration setup](https://www.figma.com/file/p81nWodxL04YD7iNyr9xVa/%2317230-Fleet-in-your-calendar?type=design&node-id=362%3A3864&mode=design&t=p2gszo7V6sbbI2nF-1). --- website/config/routes.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/website/config/routes.js b/website/config/routes.js index 6143564328..d1d490ffc7 100644 --- a/website/config/routes.js +++ b/website/config/routes.js @@ -478,6 +478,10 @@ module.exports.routes = { 'GET /learn-more-about/setup-assistant': '/docs/using-fleet/mdm-macos-setup-experience#macos-setup-assistant', 'GET /learn-more-about/policy-automations': '/docs/using-fleet/automations', 'GET /install-wine': 'https://github.com/fleetdm/fleet/blob/main/scripts/macos-install-wine.sh', + 'GET /learn-more-about/creating-service-accounts': 'https://console.cloud.google.com/projectselector2/iam-admin/serviceaccounts/create?walkthrough_id=iam--create-service-account&pli=1#step_index=1', + 'GET /learn-more-about/google-workspace-domains': 'https://admin.google.com/ac/domains/manage', + 'GET /learn-more-about/domain-wide-delegation': 'https://admin.google.com/ac/owl/domainwidedelegation', + 'GET /learn-more-about/enabling-calendar-api': 'https://console.cloud.google.com/apis/library/calendar-json.googleapis.com', // Sitemap // ============================================================================================================= From 8d0d309a1fe9efd37e32037c15bf93abfd980b62 Mon Sep 17 00:00:00 2001 From: Brock Walters <153771548+nonpunctual@users.noreply.github.com> Date: Fri, 5 Apr 2024 16:14:57 -0400 Subject: [PATCH 49/60] Update macos-install-wine.sh with codesign warning (#17982) The Wine developer does have an Apple Develeoper certificate but the "Wine Stable" app bundle is not code-signed or notarized post-install & disables Gatekeeper for the install. This adds a warning to the script user about the app not being signed. post-install --------- Co-authored-by: Victor Lyuboslavsky --- .github/workflows/test-packaging.yml | 2 +- docs/Using Fleet/enroll-hosts.md | 2 +- scripts/macos-install-wine.sh | 61 ++++++++++++++++++++++++---- 3 files changed, 54 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test-packaging.yml b/.github/workflows/test-packaging.yml index 113f3c33ce..428544fcb0 100644 --- a/.github/workflows/test-packaging.yml +++ b/.github/workflows/test-packaging.yml @@ -85,7 +85,7 @@ jobs: - name: Install wine and wix if: matrix.os == 'macos-latest' run: | - ./scripts/macos-install-wine.sh + ./scripts/macos-install-wine.sh -n wget https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip -nv -O wix.zip mkdir wix unzip wix.zip -d wix diff --git a/docs/Using Fleet/enroll-hosts.md b/docs/Using Fleet/enroll-hosts.md index 81e852cbb6..08e0827af7 100644 --- a/docs/Using Fleet/enroll-hosts.md +++ b/docs/Using Fleet/enroll-hosts.md @@ -340,7 +340,7 @@ so: ``` If the provided path doesn't contain all 3 binaries, the command will fail. ->**Note:** Creating a fleetd agent for Windows (.msi) on macOS also requires Wine. To install Wine see the script [here](https://github.com/fleetdm/fleet/blob/fleet-v4.44.0/scripts/macos-install-wine.sh). +>**Note:** Creating a fleetd agent for Windows (.msi) on macOS also requires Wine. To install Wine see the script [here](https://fleetdm.com/install-wine). ### Experimental features diff --git a/scripts/macos-install-wine.sh b/scripts/macos-install-wine.sh index 5a29f352d2..fc89c8398c 100755 --- a/scripts/macos-install-wine.sh +++ b/scripts/macos-install-wine.sh @@ -1,16 +1,59 @@ #!/usr/bin/env bash + set -eo pipefail -# Run this script in user context (not root). -# Reference: https://wiki.winehq.org/MacOS -# Wine can be installed without brew via a distribution such as https://github.com/Gcenx/macOS_Wine_builds/releases/tag/9.0, or by building from source. -# Check if brew is installed -if ! command -v brew >/dev/null 2>&1 ; then - echo "Homebrew is not installed. Please install Homebrew first. For instructions, see https://brew.sh/" - exit 1 +brew_wine(){ +# Wine reference: https://wiki.winehq.org/MacOS +# Wine can be installed without brew via a distribution such as https://github.com/Gcenx/macOS_Wine_builds/releases/tag/9.0 or by building from source. +brew install --cask --no-quarantine https://raw.githubusercontent.com/Homebrew/homebrew-cask/1ecfe82f84e0f3c3c6b741d3ddc19a164c2cb18d/Casks/w/wine-stable.rb; exit 0 +} + + +warn_wine(){ +printf "\nWARNING: The Wine app developer has an Apple Developer certificate but the\napp bundle post-installation will not be code-signed or notarized.\n\nDo you wish to proceed?\n\n" +while true +do + read -r -p "install> " install + case "$install" in + y|yes|Y|YES) brew_wine ;; + n|no|N|NO) printf "\nExiting...\n\n"; exit 1 ;; + *) printf "\nPlease enter yes or no at the prompt...\n\n" ;; + esac +done +} + + +# option to execute script in non-interactive mode +while getopts 'n' option +do + case "$option" in + n) mode=auto ;; + *) : ;; + esac +done + + +# prevent root execution +if [ "$EUID" = 0 ] +then + printf "\nTo prevent unnecessary privilege elevation do not execute this script as the root user.\nExiting...\n\n"; exit 1 +fi + + +# check if Homebrew is installed +if ! command -v brew > /dev/null 2>&1 +then + printf "\nHomebrew is not installed.\nPlease install Homebrew.\nFor instructions, see https://brew.sh/\n\n"; exit 1 +fi + + +# install Wine +if [ "$mode" = 'auto' ] +then + printf "\n%s executed in non-interactive mode.\n\n" "$0"; brew_wine +else + warn_wine fi -# Install wine via brew -brew install --cask --no-quarantine https://raw.githubusercontent.com/Homebrew/homebrew-cask/1ecfe82f84e0f3c3c6b741d3ddc19a164c2cb18d/Casks/w/wine-stable.rb From 766d08e76792c691285d7cfe6ca16aa2c96b895b Mon Sep 17 00:00:00 2001 From: Eric Date: Fri, 5 Apr 2024 15:39:02 -0500 Subject: [PATCH 50/60] Schema: remove `requires_user_context` related code, regenerate schema json. (#18091) Changes: - Updated the `build-static-content` script to remove support for the `requires_user_context` column attribute - Updated `get-extended-osquery-schema` to not set a `requires_user_context` value in the merged schema json. - Regenerated `schema/osquery_fleet_schema.json` --- schema/osquery_fleet_schema.json | 85 ++++++++----------- .../helpers/get-extended-osquery-schema.js | 3 - website/scripts/build-static-content.js | 3 - 3 files changed, 37 insertions(+), 54 deletions(-) diff --git a/schema/osquery_fleet_schema.json b/schema/osquery_fleet_schema.json index 7262cfba22..6f88e524f6 100644 --- a/schema/osquery_fleet_schema.json +++ b/schema/osquery_fleet_schema.json @@ -1715,8 +1715,8 @@ ], "evented": false, "cacheable": false, - "notes": "", - "examples": "List the SSH keys allowed to connect to this host.\n```\nSELECT key FROM authorized_keys;\n```", + "notes": "Querying this table requires joining against the `users` table. [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table)", + "examples": "```\nSELECT * FROM users CROSS JOIN authorized_keys USING (uid);\n```", "columns": [ { "name": "uid", @@ -1725,8 +1725,7 @@ "notes": "", "hidden": false, "required": false, - "index": false, - "requires_user_context": true + "index": false }, { "name": "algorithm", @@ -2838,8 +2837,8 @@ ], "evented": false, "cacheable": false, - "notes": "", - "examples": "See classic browser plugins (C/NPAPI) installed by users. These plugins have been deprecated for a long time, so this query will usually not return anything.\n```\nSELECT bp.name, bp.identifier, bp.version FROM browser_plugins bp JOIN users u on bp.uid = u.uid ;\n```", + "notes": "Querying this table requires joining against the `users` table. [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table)", + "examples": "See classic browser plugins (C/NPAPI) installed by users. These plugins have been deprecated for a long time, so this query will usually not return anything.\n```\nSELECT * FROM users CROSS JOIN browser_plugins USING (uid);\n```", "columns": [ { "name": "uid", @@ -3691,8 +3690,8 @@ ], "evented": false, "cacheable": false, - "notes": "", - "examples": "```\nSELECT chrome_extension_content_scripts.* FROM users JOIN chrome_extension_content_scripts USING (uid) GROUP BY identifier, match\n```", + "notes": "Querying this table requires joining against the `users` table. [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table)", + "examples": "```\nSELECT * FROM users CROSS JOIN chrome_extension_content_scripts USING (uid);\n```", "columns": [ { "name": "browser_type", @@ -3710,8 +3709,7 @@ "notes": "", "hidden": false, "required": false, - "index": true, - "requires_user_context": true + "index": true }, { "name": "identifier", @@ -3791,8 +3789,8 @@ ], "evented": false, "cacheable": false, - "notes": "- On ChromeOS, this table requires the [fleetd Chrome extension](https://fleetdm.com/docs/using-fleet/chromeos).\n", - "examples": "List Chrome extensions by user and profile which have full access to HTTPS browsing.\n```\nSELECT u.username, ce.name, ce.description, ce.version, ce.profile, ce.permissions FROM users u CROSS JOIN chrome_extensions ce USING (uid) WHERE ce.permissions LIKE '%%https://*/*%%';\n```", + "notes": "Querying this table requires joining against the `users` table. [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table)\n\nOn ChromeOS, this table requires the [fleetd Chrome extension](https://fleetdm.com/docs/using-fleet/chromeos).\n", + "examples": "```\nSELECT * FROM users CROSS JOIN chrome_extensions USING (uid);\n```\nList Chrome extensions by user and profile which have full access to HTTPS browsing.\n```\nSELECT u.username, ce.name, ce.description, ce.version, ce.profile, ce.permissions FROM users u CROSS JOIN chrome_extensions ce USING (uid) WHERE ce.permissions LIKE '%%https://*/*%%';\n```", "columns": [ { "name": "browser_type", @@ -3815,8 +3813,7 @@ "macOS", "Windows", "Linux" - ], - "requires_user_context": true + ] }, { "name": "name", @@ -5067,8 +5064,8 @@ ], "evented": false, "cacheable": false, - "notes": "", - "examples": "See software responsible for crashes. This can be useful to detect what the most problematic software in your environment is.\n```\nSELECT crash_path, identifier, responsible, exception_type FROM crashes;\n```", + "notes": "Querying this table requires joining against the `users` table. [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table)", + "examples": "```\nSELECT * FROM users CROSS JOIN crashes USING (uid);\n```", "columns": [ { "name": "type", @@ -5149,8 +5146,7 @@ "notes": "", "hidden": false, "required": false, - "index": true, - "requires_user_context": true + "index": true }, { "name": "datetime", @@ -10781,8 +10777,8 @@ ], "evented": false, "cacheable": false, - "notes": "", - "examples": "See Firefox extensions by user as well as information about their creator and automatic update status.\n```\nSELECT u.username, f.identifier, f.creator, f.description, f.version, f.autoupdate FROM users u CROSS JOIN firefox_addons f USING (uid) WHERE f.active='1';\n```", + "notes": "Querying this table requires joining against the `users` table. [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table)", + "examples": "```\nSELECT * FROM users CROSS JOIN firefox_addons USING (uid);\n```\nSee Firefox extensions by user as well as information about their creator and automatic update status.\n```\nSELECT u.username, f.identifier, f.creator, f.description, f.version, f.autoupdate FROM users u CROSS JOIN firefox_addons f USING (uid) WHERE f.active='1';\n```", "columns": [ { "name": "uid", @@ -10791,8 +10787,7 @@ "notes": "", "hidden": false, "required": false, - "index": false, - "requires_user_context": true + "index": false }, { "name": "name", @@ -13306,8 +13301,8 @@ ], "evented": false, "cacheable": false, - "notes": "", - "examples": "```\nselect * from users join known_hosts using (uid)\n```", + "notes": "- Querying this table requires joining against the `users` table. [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table)", + "examples": "```\nSELECT * FROM users CROSS JOIN known_hosts USING (uid);\n```", "columns": [ { "name": "uid", @@ -13316,8 +13311,7 @@ "notes": "", "hidden": false, "required": false, - "index": true, - "requires_user_context": true + "index": true }, { "name": "key", @@ -19975,8 +19969,8 @@ ], "evented": false, "cacheable": false, - "notes": "- The `value` column will be empty for keys that contain binary data.", - "examples": "This table reads a huge amount of preferences, including on third-party apps. This query will show how many users are enrolled to TouchID.\n```\nSELECT * FROM preferences WHERE subkey='dailyEvents/2/enrolledUserCount';\n```", + "notes": "- Querying this table requires joining against the `users` table. [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table)\n- The `value` column will be empty for keys that contain binary data.", + "examples": "This table reads a huge amount of preferences, including on third-party apps.\n```\nSELECT * FROM users CROSS JOIN preferences USING (username);\n```", "columns": [ { "name": "domain", @@ -20030,8 +20024,7 @@ "notes": "", "hidden": false, "required": false, - "index": false, - "requires_user_context": true + "index": false }, { "name": "host", @@ -23022,8 +23015,8 @@ ], "evented": false, "cacheable": false, - "notes": "- Includes installed extensions for all system users.", - "examples": "```\nselect count(*) from users JOIN safari_extensions using (uid)\n```", + "notes": "- Querying this table requires joining against the `users` table. [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table) - Includes installed extensions for all system users.", + "examples": "```\nSELECT * FROM users CROSS JOIN safari_extensions USING (uid);\n```", "columns": [ { "name": "uid", @@ -23032,8 +23025,7 @@ "notes": "", "hidden": false, "required": false, - "index": true, - "requires_user_context": true + "index": true }, { "name": "name", @@ -24522,8 +24514,8 @@ ], "evented": false, "cacheable": false, - "notes": "", - "examples": "See command line executions and related timestamps. Useful for threat hunting when a device is suspected of being compromised.\n```\nSELECT u.username, s.command, s.time FROM users u CROSS JOIN shell_history s USING (uid);\n```", + "notes": "- Querying this table requires joining against the `users` table. [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table)", + "examples": "```\nSELECT * FROM users CROSS JOIN shell_history USING (uid);\n```\nSee command line executions and related timestamps. Useful for threat hunting when a device is suspected of being compromised.\n```\nSELECT u.username, s.command, s.time FROM users u CROSS JOIN shell_history s USING (uid);\n```", "columns": [ { "name": "uid", @@ -24532,8 +24524,7 @@ "notes": "", "hidden": false, "required": false, - "index": false, - "requires_user_context": true + "index": false }, { "name": "time", @@ -25204,8 +25195,8 @@ ], "evented": false, "cacheable": false, - "notes": "", - "examples": "Identify SSH clients configured to send their locales to the server.\n```\nSELECT * FROM ssh_configs WHERE option='sendenv lang lc_*'; \n```", + "notes": "Querying this table requires joining against the `users` table. [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table)", + "examples": "```\nSELECT * FROM users CROSS JOIN ssh_configs USING (uid);\n```\nIdentify SSH clients configured to send their locales to the server.\n```\nSELECT * FROM ssh_configs WHERE option='sendenv lang lc_*'; \n```", "columns": [ { "name": "uid", @@ -25214,8 +25205,7 @@ "notes": "", "hidden": false, "required": false, - "index": false, - "requires_user_context": true + "index": false }, { "name": "block", @@ -27098,8 +27088,8 @@ ], "evented": false, "cacheable": false, - "notes": "", - "examples": "Identify SSH keys stored in clear text in user directories\n```\nSELECT * FROM users JOIN user_ssh_keys USING (uid) WHERE encrypted = 0;\n```", + "notes": "Querying this table requires joining against the `users` table. [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table)", + "examples": "```\nSELECT * FROM users CROSS JOIN user_ssh_keys USING (uid);\n```\nIdentify SSH keys stored in clear text in user directories\n```\nSELECT * FROM users JOIN user_ssh_keys USING (uid) WHERE encrypted = 0;\n```", "columns": [ { "name": "uid", @@ -27108,8 +27098,7 @@ "notes": "", "hidden": false, "required": false, - "index": false, - "requires_user_context": true + "index": false }, { "name": "path", @@ -27689,8 +27678,8 @@ ], "evented": false, "cacheable": false, - "notes": "Querying this table requires joining against the `users` table.", - "examples": "List the name, publisher, and version of the Visual Studio (VS) Code extensions installed on hosts.\n```\nSELECT extension.name, extension.publisher, extension.version FROM users JOIN vscode_extensions extension USING (uid);\n```", + "notes": "Querying this table requires joining against the `users` table. [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table)", + "examples": "```\nSELECT * FROM users CROSS JOIN vscode_extensions USING (uid);\n```\n\nList the name, publisher, and version of the Visual Studio (VS) Code extensions installed on hosts.\n```\nSELECT extension.name, extension.publisher, extension.version FROM users JOIN vscode_extensions extension USING (uid);\n```", "columns": [ { "name": "name", diff --git a/website/api/helpers/get-extended-osquery-schema.js b/website/api/helpers/get-extended-osquery-schema.js index d29a144614..f37e65f63f 100644 --- a/website/api/helpers/get-extended-osquery-schema.js +++ b/website/api/helpers/get-extended-osquery-schema.js @@ -243,9 +243,6 @@ module.exports = { if(columnHasFleetOverrides.hidden !== true) { // If the overrides don't explicitly hide a column, we'll set the value to false to make sure the column is visible on fleetdm.com fleetColumn.hidden = false; } - if(columnHasFleetOverrides.requires_user_context) { - fleetColumn.requires_user_context = true; // eslint-disable-line camelcase - } mergedTableColumns.push(fleetColumn); } } diff --git a/website/scripts/build-static-content.js b/website/scripts/build-static-content.js index 1143f63b6e..9342f84af6 100644 --- a/website/scripts/build-static-content.js +++ b/website/scripts/build-static-content.js @@ -723,9 +723,6 @@ module.exports = { if(column.required) { // If a column has `"required": true`, we'll add a note to the description that will be added to the table columnDescriptionForTable += '
**Required in `WHERE` clause** '; } - if(column.requires_user_context) { // If a column has `"requires_user_context": true`, we'll add a note to the description that will be added to the table - columnDescriptionForTable += '
**Defaults to root**   [Learn more](https://fleetdm.com/guides/osquery-consider-joining-against-the-users-table?utm_source=fleetdm.com&utm_content=table-'+encodeURIComponent(table.name)+')'; - } if(column.hidden) { // If a column has `"hidden": true`, we'll add a note to the description that will be added to the table columnDescriptionForTable += '
**Not returned in `SELECT * FROM '+table.name+'`.**'; } From 5a02624f278d4b4fb25524346e42500dd59976c5 Mon Sep 17 00:00:00 2001 From: Lucas Manuel Rodriguez Date: Fri, 5 Apr 2024 18:41:46 -0300 Subject: [PATCH 51/60] Add support for pre-release on Windows orbit builder (#18102) We need to support semantic versioning with pre-releases to comply with semantic versioning required by goreleaser allowing us to build orbit pre-releases. --- orbit/pkg/packaging/windows.go | 16 ++++++++++++---- orbit/pkg/packaging/windows_test.go | 8 ++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/orbit/pkg/packaging/windows.go b/orbit/pkg/packaging/windows.go index b35a00c5bb..fad9fc4e1c 100644 --- a/orbit/pkg/packaging/windows.go +++ b/orbit/pkg/packaging/windows.go @@ -385,11 +385,20 @@ func createVersionInfo(vParts []string, manifestPath string) (*goversioninfo.Ver // SanitizeVersion returns the version parts (Major, Minor, Patch and Build), filling the Build part // with '0' if missing. Will error out if the version string is missing the Major, Minor or // Patch part(s). +// It supports the version with a pre-release part (e.g. 1.2.3-1) and returns it as the Build number. func SanitizeVersion(version string) ([]string, error) { vParts := strings.Split(version, ".") if len(vParts) < 3 { return nil, errors.New("invalid version string") } + if len(vParts) == 3 && strings.Contains(vParts[2], "-") { + parts := strings.SplitN(vParts[2], "-", 2) + if len(parts) != 2 || parts[0] == "" || parts[1] == "" { + return nil, fmt.Errorf("invalid patch and pre-release version: %s", vParts[2]) + } + patch, preRelease := parts[0], parts[1] + vParts = []string{vParts[0], vParts[1], patch, preRelease} + } if len(vParts) < 4 { vParts = append(vParts, "0") @@ -465,7 +474,7 @@ func downloadAndExtractZip(client *http.Client, urlPath string, destPath string) } defer zipReader.Close() - err = os.MkdirAll(filepath.Dir(destPath), 0755) + err = os.MkdirAll(filepath.Dir(destPath), 0o755) if err != nil { return fmt.Errorf("could not create directory %s: %w", filepath.Dir(destPath), err) } @@ -479,7 +488,6 @@ func downloadAndExtractZip(client *http.Client, urlPath string, destPath string) } return nil - } func extractZipFile(archiveReader *zip.File, destPath string) error { @@ -506,13 +514,13 @@ func extractZipFile(archiveReader *zip.File, destPath string) error { // Check if the file to extract is just a directory if archiveReader.FileInfo().IsDir() { - err = os.MkdirAll(finalPath, 0755) + err = os.MkdirAll(finalPath, 0o755) if err != nil { return fmt.Errorf("could not create directory %s: %w", finalPath, err) } } else { // Create all needed directories - if os.MkdirAll(filepath.Dir(finalPath), 0755) != nil { + if os.MkdirAll(filepath.Dir(finalPath), 0o755) != nil { return fmt.Errorf("could not create directory %s: %w", filepath.Dir(finalPath), err) } diff --git a/orbit/pkg/packaging/windows_test.go b/orbit/pkg/packaging/windows_test.go index cd1ee4e54c..2b6e0729ab 100644 --- a/orbit/pkg/packaging/windows_test.go +++ b/orbit/pkg/packaging/windows_test.go @@ -75,6 +75,14 @@ func TestSanitizeVersion(t *testing.T) { }{ {Version: "4.13.0", Parts: []string{"4", "13", "0", "0"}}, {Version: "4.13.0.1", Parts: []string{"4", "13", "0", "1"}}, + + // We need to support this form of semantic versioning (with pre-releases) + // to comply with semantic versioning required by goreleaser to allow building + // orbit pre-releases. + {Version: "4.13.0-1", Parts: []string{"4", "13", "0", "1"}}, + {Version: "4.13.0-alpha", Parts: []string{"4", "13", "0", "alpha"}}, + {Version: "4.13.0-", ErrorsOut: true}, + {Version: "4.13.0.1.2", Parts: []string{"4", "13", "0", "1"}}, {Version: "4", ErrorsOut: true}, {Version: "4.13", ErrorsOut: true}, From c19ebfee4681d0bcd6ef492048e656cc9fa88069 Mon Sep 17 00:00:00 2001 From: Victor Lyuboslavsky Date: Fri, 5 Apr 2024 16:45:07 -0500 Subject: [PATCH 52/60] Update golang.org/x/net dependency to fix GO-2024-2687 (#18086) #17903 Update golang.org/x/net dependency to fix GO-2024-2687 --- go.mod | 10 +++++----- go.sum | 16 ++++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/go.mod b/go.mod index 2b89ecb1ce..cdc4b0a28e 100644 --- a/go.mod +++ b/go.mod @@ -110,16 +110,17 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 go.opentelemetry.io/otel/sdk v1.19.0 - golang.org/x/crypto v0.17.0 + golang.org/x/crypto v0.22.0 golang.org/x/exp v0.0.0-20230105202349-8879d0199aa3 golang.org/x/image v0.10.0 golang.org/x/mod v0.12.0 - golang.org/x/net v0.19.0 + golang.org/x/net v0.24.0 golang.org/x/oauth2 v0.12.0 golang.org/x/sync v0.3.0 - golang.org/x/sys v0.15.0 + golang.org/x/sys v0.19.0 golang.org/x/text v0.14.0 golang.org/x/tools v0.13.0 + google.golang.org/api v0.128.0 google.golang.org/grpc v1.58.3 gopkg.in/guregu/null.v3 v3.5.0 gopkg.in/ini.v1 v1.67.0 @@ -304,10 +305,9 @@ require ( go.opentelemetry.io/otel/trace v1.19.0 // indirect go.opentelemetry.io/proto/otlp v1.0.0 // indirect gocloud.dev v0.24.0 // indirect - golang.org/x/term v0.15.0 // indirect + golang.org/x/term v0.19.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/api v0.128.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a // indirect diff --git a/go.sum b/go.sum index 616092fefd..b42024dcc0 100644 --- a/go.sum +++ b/go.sum @@ -1313,8 +1313,8 @@ golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1425,8 +1425,8 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1576,8 +1576,8 @@ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1587,8 +1587,8 @@ golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From ffc2d9f68a058cd9e2d070ea5bc0354e0d1f7838 Mon Sep 17 00:00:00 2001 From: Victor Lyuboslavsky Date: Fri, 5 Apr 2024 16:45:22 -0500 Subject: [PATCH 53/60] Fixing frontend code scanning vulnerability alerts (#18042) #17903 - Fixing https://osv.dev/vulnerability/GHSA-crh6-fp67-6883 by updating @xmldom/xmldom@0.8.3 to @xmldom/xmldom@0.8.4 - Fixing https://osv.dev/vulnerability/GHSA-wf5p-g6vw-rhxx by overriding axios@0.21.1 to axios@0.28.0 - Fixing https://osv.dev/vulnerability/GHSA-p6mc-m468-83gw by removing lodash.set dependency by updating nock@13.2.4 to nock@13.5.4 - Fixing https://osv.dev/vulnerability/GHSA-4wf5-vphf-c2xc by updating terser from 5.12.1 to 5.14.2 - Fixing https://osv.dev/vulnerability/GHSA-566m-qj78-rww5 and https://osv.dev/vulnerability/GHSA-7fh5-64p2-3v2j by: - Updating to autoprefixer@10.4.19, node-sass-glob-importer@5.3.3, and postcss-loader@4.3.0 - Overriding css-selector-extract@3.3.6 to css-selector-extract@4.0.1 - Overriding css-node-extract@2.1.3 to css-node-extract@3.0.4 and overriding its postcss dependency to ^8.4.31 --- package.json | 13 +- yarn.lock | 1184 ++++++++++---------------------------------------- 2 files changed, 244 insertions(+), 953 deletions(-) diff --git a/package.json b/package.json index 1d44659eaf..f9e4ec1145 100644 --- a/package.json +++ b/package.json @@ -117,7 +117,7 @@ "@types/uuid": "8.3.4", "@typescript-eslint/eslint-plugin": "5.58.0", "@typescript-eslint/parser": "5.58.0", - "autoprefixer": "9.8.8", + "autoprefixer": "10.4.19", "babel-core": "7.0.0-bridge.0", "babel-eslint": "9.0.0", "babel-jest": "29.2.0", @@ -150,10 +150,9 @@ "json-loader": "0.5.7", "mini-css-extract-plugin": "2.7.5", "msw": "0.47.4", - "nock": "13.2.4", "node-bourbon": "4.2.8", - "node-sass-glob-importer": "5.3.2", - "postcss-loader": "3.0.0", + "node-sass-glob-importer": "5.3.3", + "postcss-loader": "4.3.0", "prettier": "2.2.1", "react-docgen-typescript-plugin": "1.0.5", "regenerator-runtime": "0.13.9", @@ -169,6 +168,12 @@ "webpack-cli": "5.0.1", "webpack-notifier": "1.12.0" }, + "resolutions": { + "**/css-node-extract": "~3.0.4", + "**/css-node-extract/postcss": "^8.4.31", + "**/css-selector-extract": "~4.0.1", + "**/wait-on/axios": "^0.28.0" + }, "browserslist": [ "defaults" ], diff --git a/yarn.lock b/yarn.lock index d7753d0ebe..03932cde62 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5775,9 +5775,9 @@ integrity sha512-0G7tNyS+yW8TdgHwZKlDWYXFA6OJQnoLCQvYKkQP0Q2X205PSQ6RNUj0M+1OB/9gRQaUZ/ccYfaxd0nhaWKfjw== "@xmldom/xmldom@^0.8.3": - version "0.8.3" - resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.3.tgz#beaf980612532aa9a3004aff7e428943aeaa0711" - integrity sha512-Lv2vySXypg4nfa51LY1nU8yDAGo/5YwF+EY/rUZgIbfvwVARcd67ttCM8SMsTeJy51YhHYavEq+FS6R0hW9PFQ== + version "0.8.10" + resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.10.tgz#a1337ca426aa61cef9fe15b5b28e340a72f6fa99" + integrity sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw== "@xtuc/ieee754@^1.2.0": version "1.2.0" @@ -5935,11 +5935,6 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv-errors@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" - integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== - ajv-formats@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" @@ -5947,7 +5942,7 @@ ajv-formats@^2.1.1: dependencies: ajv "^8.0.0" -ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: +ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== @@ -5959,7 +5954,7 @@ ajv-keywords@^5.0.0: dependencies: fast-deep-equal "^3.1.3" -ajv@^6.1.0, ajv@^6.10.0, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5: +ajv@^6.10.0, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -6135,21 +6130,6 @@ aria-query@^5.0.0: resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.0.0.tgz" integrity sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg== -arr-diff@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" - integrity sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA== - -arr-flatten@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - -arr-union@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" - integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q== - array-find@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/array-find/-/array-find-1.0.0.tgz" @@ -6176,11 +6156,6 @@ array-union@^2.1.0: resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array-unique@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" - integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== - array.prototype.flat@^1.2.5: version "1.2.5" resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz" @@ -6232,11 +6207,6 @@ assert@^2.0.0: object-is "^1.0.1" util "^0.12.0" -assign-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" - integrity sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw== - ast-types-flow@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" @@ -6298,18 +6268,17 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -autoprefixer@9.8.8: - version "9.8.8" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.8.tgz#fd4bd4595385fa6f06599de749a4d5f7a474957a" - integrity sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA== +autoprefixer@10.4.19: + version "10.4.19" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.19.tgz#ad25a856e82ee9d7898c59583c1afeb3fa65f89f" + integrity sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew== dependencies: - browserslist "^4.12.0" - caniuse-lite "^1.0.30001109" + browserslist "^4.23.0" + caniuse-lite "^1.0.30001599" + fraction.js "^4.3.7" normalize-range "^0.1.2" - num2fraction "^1.2.2" - picocolors "^0.2.1" - postcss "^7.0.32" - postcss-value-parser "^4.1.0" + picocolors "^1.0.0" + postcss-value-parser "^4.2.0" available-typed-arrays@^1.0.5: version "1.0.5" @@ -6330,12 +6299,14 @@ axios@1.6.0: form-data "^4.0.0" proxy-from-env "^1.1.0" -axios@^0.21.1: - version "0.21.4" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" - integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== +axios@^0.21.1, axios@^0.28.0: + version "0.28.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.28.1.tgz#2a7bcd34a3837b71ee1a5ca3762214b86b703e70" + integrity sha512-iUcGA5a7p0mVb4Gm/sy+FSECNkPFT4y7wt6OM/CDpO/OnNCvSs3PoMG8ibrC9jRoGYU0gUK5pXVC4NPXq6lHRQ== dependencies: - follow-redirects "^1.14.0" + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" axobject-query@^0.1.0: version "0.1.0" @@ -6585,19 +6556,6 @@ base64-js@^1.0.2, base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -base@^0.11.1: - version "0.11.2" - resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" - integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== - dependencies: - cache-base "^1.0.1" - class-utils "^0.3.5" - component-emitter "^1.2.1" - define-property "^1.0.0" - isobject "^3.0.1" - mixin-deep "^1.2.0" - pascalcase "^0.1.1" - better-opn@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/better-opn/-/better-opn-3.0.2.tgz#f96f35deaaf8f34144a4102651babcf00d1d8817" @@ -6694,22 +6652,6 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^2.3.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" - integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== - dependencies: - arr-flatten "^1.1.0" - array-unique "^0.3.2" - extend-shallow "^2.0.1" - fill-range "^4.0.0" - isobject "^3.0.1" - repeat-element "^1.1.2" - snapdragon "^0.8.1" - snapdragon-node "^2.0.1" - split-string "^3.0.2" - to-regex "^3.0.1" - braces@^3.0.2, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" @@ -6814,7 +6756,7 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.21.3: +browserslist@^4.14.5, browserslist@^4.21.3: version "4.21.4" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== @@ -6844,6 +6786,16 @@ browserslist@^4.21.9, browserslist@^4.22.1: node-releases "^2.0.13" update-browserslist-db "^1.0.13" +browserslist@^4.23.0: + version "4.23.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.0.tgz#8f3acc2bbe73af7213399430890f86c63a5674ab" + integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ== + dependencies: + caniuse-lite "^1.0.30001587" + electron-to-chromium "^1.4.668" + node-releases "^2.0.14" + update-browserslist-db "^1.0.13" + bser@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" @@ -6969,21 +6921,6 @@ cacache@^16.1.0: tar "^6.1.11" unique-filename "^2.0.0" -cache-base@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" - integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== - dependencies: - collection-visit "^1.0.0" - component-emitter "^1.2.1" - get-value "^2.0.6" - has-value "^1.0.0" - isobject "^3.0.1" - set-value "^2.0.0" - to-object-path "^0.3.0" - union-value "^1.0.0" - unset-value "^1.0.0" - caching-transform@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/caching-transform/-/caching-transform-4.0.0.tgz#00d297a4206d71e2163c39eaffa8157ac0651f0f" @@ -7002,38 +6939,11 @@ call-bind@^1.0.0, call-bind@^1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" -caller-callsite@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" - integrity sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ== - dependencies: - callsites "^2.0.0" - -caller-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" - integrity sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A== - dependencies: - caller-callsite "^2.0.0" - -callsites@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" - integrity sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ== - callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -camel-case@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" - integrity sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w== - dependencies: - no-case "^2.2.0" - upper-case "^1.1.1" - camel-case@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz" @@ -7066,7 +6976,7 @@ can-bind-to-host@^1.1.1: resolved "https://registry.yarnpkg.com/can-bind-to-host/-/can-bind-to-host-1.1.2.tgz#45919a1fb426eb1b709ddd4853cd5eda0549e606" integrity sha512-CqsgmaqiyFRNtP17Ihqa/uHbZxRirntNVNl/kJz31DLKuNRfzvzionkLoUSkElQ6Cz+cpXKA3mhHq4tjbieujA== -caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001400: +caniuse-lite@^1.0.30001400: version "1.0.30001474" resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001474.tgz" integrity sha512-iaIZ8gVrWfemh5DG3T9/YqarVZoYf0r188IjaGwx68j4Pf0SGY6CQkmJUIE+NZHkkecQGohzXmBGEwWDr9aM3Q== @@ -7081,6 +6991,20 @@ caniuse-lite@^1.0.30001541: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001559.tgz#95a982440d3d314c471db68d02664fb7536c5a30" integrity sha512-cPiMKZgqgkg5LY3/ntGeLFUpi6tzddBNS58A4tnTgQw1zON7u2sZMU7SzOeVH4tj20++9ggL+V6FDOFMTaFFYA== +caniuse-lite@^1.0.30001587, caniuse-lite@^1.0.30001599: + version "1.0.30001605" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001605.tgz#ca12d7330dd8bcb784557eb9aa64f0037870d9d6" + integrity sha512-nXwGlFWo34uliI9z3n6Qc0wZaf7zaZWA1CPZ169La5mV3I/gem7bst0vr5XQH5TJXZIMfDeZyOrZnSlVzKxxHQ== + +capital-case@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/capital-case/-/capital-case-1.0.4.tgz#9d130292353c9249f6b00fa5852bee38a717e669" + integrity sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + upper-case-first "^2.0.2" + case-sensitive-paths-webpack-plugin@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz#db64066c6422eed2e08cc14b986ca43796dbc6d4" @@ -7110,7 +7034,7 @@ chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: +chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -7140,29 +7064,23 @@ chalk@^5.2.0: resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== -change-case@^3.0.1: - version "3.1.0" - resolved "https://registry.yarnpkg.com/change-case/-/change-case-3.1.0.tgz#0e611b7edc9952df2e8513b27b42de72647dd17e" - integrity sha512-2AZp7uJZbYEzRPsFoa+ijKdvp9zsrnnt6+yFokfwEpeJm0xuJDVoxiRCAaTzyJND8GJkofo2IcKWaUZ/OECVzw== +change-case@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/change-case/-/change-case-4.1.2.tgz#fedfc5f136045e2398c0410ee441f95704641e12" + integrity sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A== dependencies: - camel-case "^3.0.0" - constant-case "^2.0.0" - dot-case "^2.1.0" - header-case "^1.0.0" - is-lower-case "^1.1.0" - is-upper-case "^1.1.0" - lower-case "^1.1.1" - lower-case-first "^1.0.0" - no-case "^2.3.2" - param-case "^2.1.0" - pascal-case "^2.0.0" - path-case "^2.1.0" - sentence-case "^2.1.0" - snake-case "^2.1.0" - swap-case "^1.1.0" - title-case "^2.1.0" - upper-case "^1.1.1" - upper-case-first "^1.1.0" + camel-case "^4.1.2" + capital-case "^1.0.4" + constant-case "^3.0.4" + dot-case "^3.0.4" + header-case "^2.0.4" + no-case "^3.0.4" + param-case "^3.0.4" + pascal-case "^3.1.2" + path-case "^3.0.4" + sentence-case "^3.0.4" + snake-case "^3.0.4" + tslib "^2.0.3" char-regex@^1.0.2: version "1.0.2" @@ -7242,16 +7160,6 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== -class-utils@^0.3.5: - version "0.3.6" - resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" - integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== - dependencies: - arr-union "^3.1.0" - define-property "^0.2.5" - isobject "^3.0.0" - static-extend "^0.1.1" - classnames@2.2.5: version "2.2.5" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d" @@ -7361,14 +7269,6 @@ collect-v8-coverage@^1.0.0: resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== -collection-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" - integrity sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw== - dependencies: - map-visit "^1.0.0" - object-visit "^1.0.0" - color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -7465,11 +7365,6 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== -component-emitter@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" - integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== - compressible@~2.0.16: version "2.0.18" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" @@ -7515,13 +7410,14 @@ console-control-strings@^1.1.0: resolved "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= -constant-case@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-2.0.0.tgz#4175764d389d3fa9c8ecd29186ed6005243b6a46" - integrity sha512-eS0N9WwmjTqrOmR3o83F5vW8Z+9R1HnVz3xmzT2PMFug9ly+Au/fxRWlEBSb6LcZwspSsEn9Xs1uw9YgzAg1EQ== +constant-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.4.tgz#3b84a9aeaf4cf31ec45e6bf5de91bdfb0589faf1" + integrity sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ== dependencies: - snake-case "^2.1.0" - upper-case "^1.1.1" + no-case "^3.0.4" + tslib "^2.0.3" + upper-case "^2.0.2" constants-browserify@^1.0.0: version "1.0.0" @@ -7565,11 +7461,6 @@ cookie@^0.4.2: resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== -copy-descriptor@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" - integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== - core-js-compat@^3.25.1: version "3.30.1" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.30.1.tgz#961541e22db9c27fc48bfc13a3cafa8734171dfe" @@ -7604,16 +7495,6 @@ core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== -cosmiconfig@^5.0.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" - integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== - dependencies: - import-fresh "^2.0.0" - is-directory "^0.3.1" - js-yaml "^3.13.1" - parse-json "^4.0.0" - cosmiconfig@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz" @@ -7736,13 +7617,13 @@ css-loader@6.7.3, css-loader@^6.7.1: postcss-value-parser "^4.2.0" semver "^7.3.8" -css-node-extract@^2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/css-node-extract/-/css-node-extract-2.1.3.tgz#ec388a857b8fdf13fefd94b3da733257162405da" - integrity sha512-E7CzbC0I4uAs2dI8mPCVe+K37xuja5kjIugOotpwICFL7vzhmFMAPHvS/MF9gFrmv8DDUANsxrgyT/I3OLukcw== +css-node-extract@^2.1.3, css-node-extract@~3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/css-node-extract/-/css-node-extract-3.0.4.tgz#896e454cf1626662fb373046d2464c90c340f842" + integrity sha512-y0IUmFXrKOMkJNZHtvU9aKXrMUP6q60TB75S0FRqywovIxNl9i8spVsmpsMJ7uwbYn+0VmRp8aPRwcB1XRbo4g== dependencies: - change-case "^3.0.1" - postcss "^6.0.14" + change-case "^4.1.2" + postcss "^7.0.36" css-select@^4.1.3: version "4.2.1" @@ -7755,12 +7636,12 @@ css-select@^4.1.3: domutils "^2.8.0" nth-check "^2.0.1" -css-selector-extract@^3.3.6: - version "3.3.6" - resolved "https://registry.yarnpkg.com/css-selector-extract/-/css-selector-extract-3.3.6.tgz#5cc670cfeae743015e80faf2d722d7818657e3e5" - integrity sha512-bBI8ZJKKyR9iHvxXb4t3E6WTMkis94eINopVg7y2FmmMjLXUVduD7mPEcADi4i9FX4wOypFMFpySX+0keuefxg== +css-selector-extract@^3.3.6, css-selector-extract@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/css-selector-extract/-/css-selector-extract-4.0.1.tgz#71f62916435764cd5eb338bdd962f6fef01b8795" + integrity sha512-Ee2PJUE0Ma9NqCZ5AauYIsKZJA8Ni81XIbwoypv3Iid4MAPfRmjYBLxpRWsAi10D/iPIWZ5DIRGJ8dj/mRGsCQ== dependencies: - postcss "^6.0.14" + postcss "^8.2.13" css-what@^5.1.0: version "5.1.0" @@ -7849,7 +7730,7 @@ date-fns@2.28.0: resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.28.0.tgz#9570d656f5fc13143e50c975a3b6bbeb46cd08b2" integrity sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw== -debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: +debug@2.6.9, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -7962,28 +7843,6 @@ define-properties@^1.1.3, define-properties@^1.1.4, define-properties@~1.1.2: has-property-descriptors "^1.0.0" object-keys "^1.1.1" -define-property@^0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" - integrity sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA== - dependencies: - is-descriptor "^0.1.0" - -define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" - integrity sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA== - dependencies: - is-descriptor "^1.0.0" - -define-property@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" - integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== - dependencies: - is-descriptor "^1.0.2" - isobject "^3.0.1" - defu@^6.1.2: version "6.1.2" resolved "https://registry.yarnpkg.com/defu/-/defu-6.1.2.tgz#1217cba167410a1765ba93893c6dbac9ed9d9e5c" @@ -8255,13 +8114,6 @@ domutils@^2.5.2, domutils@^2.8.0: domelementtype "^2.2.0" domhandler "^4.2.0" -dot-case@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-2.1.1.tgz#34dcf37f50a8e93c2b3bca8bb7fb9155c7da3bee" - integrity sha512-HnM6ZlFqcajLsyudHq7LeeLDr2rFAVYtDv/hV5qchQEidSck8j9OPUsXY9KwJv/lHMtYlX4DjRQqwFYa+0r8Ug== - dependencies: - no-case "^2.2.0" - dot-case@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz" @@ -8322,6 +8174,11 @@ electron-to-chromium@^1.4.535: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.572.tgz#ed9876658998138fe9e3aa47ecfa0bf914192a86" integrity sha512-RlFobl4D3ieetbnR+2EpxdzFl9h0RAJkPK3pfiwMug2nhBin2ZCsGIAJWdpNniLz43sgXam/CgipOmvTA+rUiA== +electron-to-chromium@^1.4.668: + version "1.4.724" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.724.tgz#e0a86fe4d3d0e05a4d7b032549d79608078f830d" + integrity sha512-RTRvkmRkGhNBPPpdrgtDKvmOEYTrPlXDfc0J/Nfq5s29tEahAwhiX4mmhNzj6febWMleulxVYPh7QwCSL/EldA== + elliptic@^6.5.3, elliptic@^6.5.4: version "6.5.4" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" @@ -9127,19 +8984,6 @@ exit@^0.1.2: resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== -expand-brackets@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" - integrity sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA== - dependencies: - debug "^2.3.3" - define-property "^0.2.5" - extend-shallow "^2.0.1" - posix-character-classes "^0.1.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - expand-tilde@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-1.2.2.tgz#0b81eba897e5a3d31d1c3d102f8f01441e559449" @@ -9231,21 +9075,6 @@ express@4.19.2, express@^4.17.3: utils-merge "1.0.1" vary "~1.1.2" -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== - dependencies: - is-extendable "^0.1.0" - -extend-shallow@^3.0.0, extend-shallow@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" - integrity sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q== - dependencies: - assign-symbols "^1.0.0" - is-extendable "^1.0.1" - extend@^3.0.0: version "3.0.2" resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" @@ -9260,20 +9089,6 @@ external-editor@^3.0.3: iconv-lite "^0.4.24" tmp "^0.0.33" -extglob@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" - integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== - dependencies: - array-unique "^0.3.2" - define-property "^1.0.0" - expand-brackets "^2.1.4" - extend-shallow "^2.0.1" - fragment-cache "^0.2.1" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - extract-zip@^1.6.6: version "1.7.0" resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927" @@ -9392,16 +9207,6 @@ filelist@^1.0.1: dependencies: minimatch "^5.0.1" -fill-range@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" - integrity sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ== - dependencies: - extend-shallow "^2.0.1" - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range "^2.1.0" - fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -9499,14 +9304,14 @@ find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" -findup-sync@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz" - integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg== +findup-sync@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-4.0.0.tgz#956c9cdde804052b881b428512905c4a5f2cdef0" + integrity sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ== dependencies: detect-file "^1.0.0" is-glob "^4.0.0" - micromatch "^3.0.4" + micromatch "^4.0.2" resolve-dir "^1.0.1" flat-cache@^3.0.4: @@ -9527,7 +9332,7 @@ flow-parser@0.*: resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.205.0.tgz#8756173b6488dedc31ab838e80c8f008d7a44e05" integrity sha512-ZJ6VuLe/BoqeI4GsF+ZuzlpfGi3FCnBrb4xDYhgEJxRt7SAj3ibRuRSsuJSRcY+lQhPZRPNbNWiQqFMxramUzw== -follow-redirects@^1.14.0, follow-redirects@^1.15.0: +follow-redirects@^1.15.0: version "1.15.6" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== @@ -9539,11 +9344,6 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" -for-in@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" - integrity sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== - foreground-child@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz" @@ -9620,12 +9420,10 @@ forwarded@0.2.0: resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== -fragment-cache@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" - integrity sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA== - dependencies: - map-cache "^0.2.2" +fraction.js@^4.3.7: + version "4.3.7" + resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" + integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== fresh@0.5.2: version "0.5.2" @@ -9712,6 +9510,11 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + function.prototype.name@^1.1.5: version "1.1.5" resolved "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz" @@ -9810,11 +9613,6 @@ get-symbol-description@^1.0.0: call-bind "^1.0.2" get-intrinsic "^1.1.1" -get-value@^2.0.3, get-value@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" - integrity sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA== - giget@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/giget/-/giget-1.1.2.tgz#f99a49cb0ff85479c8c3612cdc7ca27f2066e818" @@ -10086,37 +9884,6 @@ has-unicode@^2.0.1: resolved "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz" integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= -has-value@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" - integrity sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q== - dependencies: - get-value "^2.0.3" - has-values "^0.1.4" - isobject "^2.0.0" - -has-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" - integrity sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw== - dependencies: - get-value "^2.0.6" - has-values "^1.0.0" - isobject "^3.0.0" - -has-values@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" - integrity sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ== - -has-values@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" - integrity sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ== - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - has@^1.0.1, has@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" @@ -10149,6 +9916,13 @@ hasha@^5.0.0: is-stream "^2.0.0" type-fest "^0.8.0" +hasown@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + hast-util-whitespace@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-2.0.0.tgz#4fc1086467cc1ef5ba20673cb6b03cec3a970f1c" @@ -10167,13 +9941,13 @@ he@^1.2.0: resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -header-case@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/header-case/-/header-case-1.0.1.tgz#9535973197c144b09613cd65d317ef19963bd02d" - integrity sha512-i0q9mkOeSuhXw6bGgiQCCBgY/jlZuV/7dZXyZ9c6LcBrqwvT8eT719E9uxE5LiZftdl+z81Ugbg/VvXV4OJOeQ== +header-case@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/header-case/-/header-case-2.0.4.tgz#5a42e63b55177349cf405beb8d775acabb92c063" + integrity sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q== dependencies: - no-case "^2.2.0" - upper-case "^1.1.3" + capital-case "^1.0.4" + tslib "^2.0.3" headers-polyfill@^3.1.0: version "3.1.2" @@ -10456,21 +10230,6 @@ ignore@^5.2.0: resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz" integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== -import-cwd@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" - integrity sha512-Ew5AZzJQFqrOV5BTW3EIoHAnoie1LojZLXKcCQ/yTRyVZosBhK1x1ViYjHGf5pAFOq8ZyChZp6m/fSN7pJyZtg== - dependencies: - import-from "^2.1.0" - -import-fresh@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" - integrity sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg== - dependencies: - caller-path "^2.0.0" - resolve-from "^3.0.0" - import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" @@ -10479,13 +10238,6 @@ import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" -import-from@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-from/-/import-from-2.1.0.tgz#335db7f2a7affd53aaa471d4b8021dee36b7f3b1" - integrity sha512-0vdnLL2wSGnhlRmzHJAg5JHjt1l2vYhzJ7tNLGbeVg0fse56tpGaH0uzH+r9Slej+BSXXEHvBKDEnVSLLE9/+w== - dependencies: - resolve-from "^3.0.0" - import-local@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" @@ -10604,20 +10356,6 @@ is-absolute-url@^3.0.0: resolved "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz" integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== -is-accessor-descriptor@^0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" - integrity sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A== - dependencies: - kind-of "^3.0.2" - -is-accessor-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" - integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== - dependencies: - kind-of "^6.0.0" - is-arguments@^1.0.4, is-arguments@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz" @@ -10676,11 +10414,6 @@ is-boolean-object@^1.1.0, is-boolean-object@^1.1.2: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - is-buffer@^2.0.0: version "2.0.5" resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz" @@ -10698,6 +10431,13 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" +is-core-module@^2.13.0: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== + dependencies: + hasown "^2.0.0" + is-core-module@^2.2.0, is-core-module@^2.5.0, is-core-module@^2.8.0, is-core-module@^2.9.0: version "2.10.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed" @@ -10705,20 +10445,6 @@ is-core-module@^2.2.0, is-core-module@^2.5.0, is-core-module@^2.8.0, is-core-mod dependencies: has "^1.0.3" -is-data-descriptor@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" - integrity sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg== - dependencies: - kind-of "^3.0.2" - -is-data-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" - integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== - dependencies: - kind-of "^6.0.0" - is-date-object@^1.0.1, is-date-object@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz" @@ -10731,29 +10457,6 @@ is-deflate@^1.0.0: resolved "https://registry.yarnpkg.com/is-deflate/-/is-deflate-1.0.0.tgz#c862901c3c161fb09dac7cdc7e784f80e98f2f14" integrity sha512-YDoFpuZWu1VRXlsnlYMzKyVRITXj7Ej/V9gXQ2/pAe7X1J7M/RNOqaIYi6qUn+B7nGyB9pDXrv02dsB58d2ZAQ== -is-descriptor@^0.1.0: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" - integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== - dependencies: - is-accessor-descriptor "^0.1.6" - is-data-descriptor "^0.1.4" - kind-of "^5.0.0" - -is-descriptor@^1.0.0, is-descriptor@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" - integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== - dependencies: - is-accessor-descriptor "^1.0.0" - is-data-descriptor "^1.0.0" - kind-of "^6.0.2" - -is-directory@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" - integrity sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw== - is-docker@^2.0.0, is-docker@^2.1.1: version "2.2.1" resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" @@ -10786,18 +10489,6 @@ is-equal@^1.5.1: which-boxed-primitive "^1.0.2" which-collection "^1.0.1" -is-extendable@^0.1.0, is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== - -is-extendable@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" - integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== - dependencies: - is-plain-object "^2.0.4" - is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -10849,13 +10540,6 @@ is-lambda@^1.0.1: resolved "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz" integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU= -is-lower-case@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/is-lower-case/-/is-lower-case-1.1.3.tgz#7e147be4768dc466db3bfb21cc60b31e6ad69393" - integrity sha512-+5A1e/WJpLLXZEDlgz4G//WYSHyQBD32qa4Jd3Lw06qQlv3fJHnp3YIHjTQSGzHMgzmVKz2ZP3rBxTHkPw/lxA== - dependencies: - lower-case "^1.1.0" - is-map@^2.0.1, is-map@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz" @@ -10886,13 +10570,6 @@ is-number-object@^1.0.4, is-number-object@^1.0.6: dependencies: has-tostringtag "^1.0.0" -is-number@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" - integrity sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg== - dependencies: - kind-of "^3.0.2" - is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -10923,7 +10600,7 @@ is-plain-object@5.0.0: resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== -is-plain-object@^2.0.3, is-plain-object@^2.0.4: +is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== @@ -10995,13 +10672,6 @@ is-unicode-supported@^0.1.0: resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== -is-upper-case@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/is-upper-case/-/is-upper-case-1.1.2.tgz#8d0b1fa7e7933a1e58483600ec7d9661cbaf756f" - integrity sha512-GQYSJMgfeAmVwh9ixyk888l7OIhNAGKtY6QA+IrWlu9MDTCaXmeozOZ2S9Knj7bQwBO/H6J2kb+pbyTUiMNbsw== - dependencies: - upper-case "^1.1.0" - is-weakmap@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" @@ -11044,7 +10714,7 @@ isarray@0.0.1: resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= -isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: +isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== @@ -11059,14 +10729,7 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - integrity sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA== - dependencies: - isarray "1.0.0" - -isobject@^3.0.0, isobject@^3.0.1: +isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== @@ -12290,11 +11953,6 @@ json-loader@0.5.7: resolved "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz" integrity sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w== -json-parse-better-errors@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== - json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" @@ -12315,11 +11973,6 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= -json-stringify-safe@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - json5@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" @@ -12359,26 +12012,7 @@ jsx-ast-utils@^1.4.0: array-includes "^3.1.3" object.assign "^4.1.2" -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== - dependencies: - is-buffer "^1.1.5" - -kind-of@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" - integrity sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw== - dependencies: - is-buffer "^1.1.5" - -kind-of@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" - integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== - -kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: +kind-of@^6.0.2, kind-of@^6.0.3: version "6.0.3" resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== @@ -12393,7 +12027,7 @@ kleur@^4.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780" integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ== -klona@^2.0.6: +klona@^2.0.4, klona@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.6.tgz#85bffbf819c03b2f53270412420a4555ef882e22" integrity sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA== @@ -12463,7 +12097,7 @@ loader-runner@^4.2.0: resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz" integrity sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw== -loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.4.0: +loader-utils@^1.0.2, loader-utils@^1.4.0: version "1.4.2" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3" integrity sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg== @@ -12536,11 +12170,6 @@ lodash.merge@^4.6.2: resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash.set@^4.3.2: - version "4.3.2" - resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" - integrity sha512-4hNPN5jlm/N/HLMCO43v8BXKq9Z7QdAGc/VGrRD61w8gN9g/6jF9A4L1pbUgBLCffi0w9VsXfTOij5x8iTyFvg== - lodash.truncate@^4.4.2: version "4.4.2" resolved "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz" @@ -12578,18 +12207,6 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3 dependencies: js-tokens "^3.0.0 || ^4.0.0" -lower-case-first@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/lower-case-first/-/lower-case-first-1.0.2.tgz#e5da7c26f29a7073be02d52bac9980e5922adfa1" - integrity sha512-UuxaYakO7XeONbKrZf5FEgkantPf5DUqDayzP5VXZrtRPdH86s4kN47I8B3TW10S4QKiE3ziHNf3kRN//okHjA== - dependencies: - lower-case "^1.1.2" - -lower-case@^1.1.0, lower-case@^1.1.1, lower-case@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" - integrity sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA== - lower-case@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz" @@ -12697,11 +12314,6 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" -map-cache@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" - integrity sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg== - map-obj@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz" @@ -12717,13 +12329,6 @@ map-or-similar@^1.5.0: resolved "https://registry.npmjs.org/map-or-similar/-/map-or-similar-1.5.0.tgz" integrity sha1-beJlMXSt+12e3DPGnT6Sobdvrwg= -map-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" - integrity sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w== - dependencies: - object-visit "^1.0.0" - markdown-table@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-3.0.2.tgz#9b59eb2c1b22fe71954a65ff512887065a7bb57c" @@ -13266,25 +12871,6 @@ micromark@^3.0.0: micromark-util-types "^1.0.1" uvu "^0.5.0" -micromatch@^3.0.4: - version "3.1.10" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" - integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - braces "^2.3.1" - define-property "^2.0.2" - extend-shallow "^3.0.2" - extglob "^2.0.4" - fragment-cache "^0.2.1" - kind-of "^6.0.2" - nanomatch "^1.2.9" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.2" - micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" @@ -13472,14 +13058,6 @@ minizlib@^2.0.0, minizlib@^2.1.1, minizlib@^2.1.2: minipass "^3.0.0" yallist "^4.0.0" -mixin-deep@^1.2.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" - integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== - dependencies: - for-in "^1.0.2" - is-extendable "^1.0.1" - mkdirp-classic@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" @@ -13560,27 +13138,10 @@ nano-time@1.0.0: dependencies: big-integer "^1.6.16" -nanoid@^3.3.4: - version "3.3.6" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" - integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== - -nanomatch@^1.2.9: - version "1.2.13" - resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" - integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - define-property "^2.0.2" - extend-shallow "^3.0.2" - fragment-cache "^0.2.1" - is-windows "^1.0.2" - kind-of "^6.0.2" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" +nanoid@^3.3.7: + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== natural-compare-lite@^1.4.0: version "1.4.0" @@ -13602,13 +13163,6 @@ neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1, neo-async@^2.6.2: resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -no-case@^2.2.0, no-case@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" - integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== - dependencies: - lower-case "^1.1.1" - no-case@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz" @@ -13617,16 +13171,6 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" -nock@13.2.4: - version "13.2.4" - resolved "https://registry.yarnpkg.com/nock/-/nock-13.2.4.tgz#43a309d93143ee5cdcca91358614e7bde56d20e1" - integrity sha512-8GPznwxcPNCH/h8B+XZcKjYPXnUV5clOKCjAqyjsiqA++MpNx9E9+t8YPp0MbThO+KauRo7aZJ1WuIZmOrT2Ug== - dependencies: - debug "^4.1.0" - json-stringify-safe "^5.0.1" - lodash.set "^4.3.2" - propagate "^2.0.0" - node-abort-controller@^3.0.1: version "3.1.1" resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548" @@ -13739,6 +13283,11 @@ node-releases@^2.0.13: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== + node-releases@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" @@ -13749,25 +13298,25 @@ node-releases@^2.0.8: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== -node-sass-glob-importer@5.3.2: - version "5.3.2" - resolved "https://registry.yarnpkg.com/node-sass-glob-importer/-/node-sass-glob-importer-5.3.2.tgz#465581e46027c0e9520e6d87f7e6eda858a14acb" - integrity sha512-QTX7KPsISgp55REV6pMH703nzHfWCOEYEQC0cDyTRo7XO6WDvyC0OAzekuQ4gs505IZcxv9KxZ3uPJ5s5H9D3g== +node-sass-glob-importer@5.3.3: + version "5.3.3" + resolved "https://registry.yarnpkg.com/node-sass-glob-importer/-/node-sass-glob-importer-5.3.3.tgz#08a4bc0185b6648e4da0e2b52c2715142c66bd6a" + integrity sha512-888UlmX8fiGMCQ+8/w6e3VjPlmuIPBQfbupAGnG0kfQbpto9OtofULN1xCmlvKD5e38Sz+PpETXqgogheqAMqQ== dependencies: - node-sass-magic-importer "^5.3.2" + node-sass-magic-importer "^5.3.3" -node-sass-magic-importer@^5.3.2: - version "5.3.2" - resolved "https://registry.yarnpkg.com/node-sass-magic-importer/-/node-sass-magic-importer-5.3.2.tgz#2f2248bb2e5cdb275ba34102ebf995edadf99175" - integrity sha512-T3wTUdUoXQE3QN+EsyPpUXRI1Gj1lEsrySQ9Kzlzi15QGKi+uRa9fmvkcSy2y3BKgoj//7Mt9+s+7p0poMpg6Q== +node-sass-magic-importer@^5.3.3: + version "5.3.3" + resolved "https://registry.yarnpkg.com/node-sass-magic-importer/-/node-sass-magic-importer-5.3.3.tgz#247541149c3230a6f5e8f110ab39afffb41af583" + integrity sha512-xB4yH7laj00SBIZO9Hwke3XDSqMcz+6IM7TgcxU9Ri6m6Pn8MBWwgG5HLmgZkQX3W2osUhx+k7WSOzzunuTKVw== dependencies: css-node-extract "^2.1.3" css-selector-extract "^3.3.6" - findup-sync "^3.0.0" - glob "^7.1.3" - object-hash "^1.3.1" - postcss-scss "^2.0.0" - resolve "^1.10.1" + findup-sync "^4.0.0" + glob "^7.1.6" + object-hash "^2.0.3" + postcss-scss "^3.0.2" + resolve "^1.17.0" node-sass@8.0.0: version "8.0.0" @@ -13855,11 +13404,6 @@ nth-check@^2.0.1: dependencies: boolbase "^1.0.0" -num2fraction@^1.2.2: - version "1.2.2" - resolved "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz" - integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= - nwsapi@^2.2.0, nwsapi@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0" @@ -13903,19 +13447,10 @@ object-assign@^4.1.0, object-assign@^4.1.1: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-copy@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" - integrity sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ== - dependencies: - copy-descriptor "^0.1.0" - define-property "^0.2.5" - kind-of "^3.0.3" - -object-hash@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df" - integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA== +object-hash@^2.0.3: + version "2.2.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" + integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== object-inspect@^1.1.0, object-inspect@^1.12.0, object-inspect@^1.12.2, object-inspect@^1.9.0: version "1.12.3" @@ -13935,13 +13470,6 @@ object-keys@^1.0.9, object-keys@^1.1.1: resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object-visit@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" - integrity sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA== - dependencies: - isobject "^3.0.0" - object.assign@^4.1.0, object.assign@^4.1.2, object.assign@^4.1.4: version "4.1.4" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" @@ -13988,13 +13516,6 @@ object.hasown@^1.1.0: define-properties "^1.1.3" es-abstract "^1.19.1" -object.pick@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" - integrity sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ== - dependencies: - isobject "^3.0.1" - object.values@^1.1.5: version "1.1.5" resolved "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz" @@ -14201,13 +13722,6 @@ pako@~1.0.5: resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== -param-case@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" - integrity sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w== - dependencies: - no-case "^2.2.0" - param-case@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz" @@ -14234,14 +13748,6 @@ parse-asn1@^5.0.0, parse-asn1@^5.1.6: pbkdf2 "^3.0.3" safe-buffer "^5.1.1" -parse-json@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" - integrity sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw== - dependencies: - error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" - parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" @@ -14274,14 +13780,6 @@ parseurl@~1.3.3: resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== -pascal-case@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-2.0.1.tgz#2d578d3455f660da65eca18ef95b4e0de912761e" - integrity sha512-qjS4s8rBOJa2Xm0jmxXiyh1+OFf6ekCWOvUaRgAQSktzlTbMotS0nmG9gyYAybCWBcuP4fsBeRCKNwGBnMe2OQ== - dependencies: - camel-case "^3.0.0" - upper-case-first "^1.1.0" - pascal-case@^3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz" @@ -14290,11 +13788,6 @@ pascal-case@^3.1.2: no-case "^3.0.4" tslib "^2.0.3" -pascalcase@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" - integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== - path-browserify@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" @@ -14305,12 +13798,13 @@ path-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== -path-case@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/path-case/-/path-case-2.1.1.tgz#94b8037c372d3fe2906e465bb45e25d226e8eea5" - integrity sha512-Ou0N05MioItesaLr9q8TtHVWmJ6fxWdqKB2RohFmNWVyJ+2zeKIeDNWAN6B/Pe7wpzWChhZX6nONYmOnMeJQ/Q== +path-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/path-case/-/path-case-3.0.4.tgz#9168645334eb942658375c56f80b4c0cb5f82c6f" + integrity sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg== dependencies: - no-case "^2.2.0" + dot-case "^3.0.4" + tslib "^2.0.3" path-exists@^3.0.0: version "3.0.0" @@ -14407,11 +13901,6 @@ performance-now@^2.1.0: resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -picocolors@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz" - integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA== - picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -14474,28 +13963,16 @@ polished@^4.2.2: dependencies: "@babel/runtime" "^7.17.8" -posix-character-classes@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" - integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== - -postcss-load-config@^2.0.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.1.2.tgz#c5ea504f2c4aef33c7359a34de3573772ad7502a" - integrity sha512-/rDeGV6vMUo3mwJZmeHfEDvwnTKKqQ0S7OHUi/kJvvtx3aWtyWG2/0ZWnzCt2keEclwN6Tf0DST2v9kITdOKYw== +postcss-loader@4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-4.3.0.tgz#2c4de9657cd4f07af5ab42bd60a673004da1b8cc" + integrity sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q== dependencies: - cosmiconfig "^5.0.0" - import-cwd "^2.0.0" - -postcss-loader@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-3.0.0.tgz#6b97943e47c72d845fa9e03f273773d4e8dd6c2d" - integrity sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA== - dependencies: - loader-utils "^1.1.0" - postcss "^7.0.0" - postcss-load-config "^2.0.0" - schema-utils "^1.0.0" + cosmiconfig "^7.0.0" + klona "^2.0.4" + loader-utils "^2.0.0" + schema-utils "^3.0.0" + semver "^7.3.4" postcss-modules-extract-imports@^3.0.0: version "3.0.0" @@ -14525,12 +14002,12 @@ postcss-modules-values@^4.0.0: dependencies: icss-utils "^5.0.0" -postcss-scss@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-2.1.1.tgz#ec3a75fa29a55e016b90bf3269026c53c1d2b383" - integrity sha512-jQmGnj0hSGLd9RscFw9LyuSVAa5Bl1/KBPqG1NQw9w8ND55nY4ZEsdlVuYJvLPpV+y0nwTV5v/4rHPzZRihQbA== +postcss-scss@^3.0.2: + version "3.0.5" + resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-3.0.5.tgz#bd484faf05890e48a6f5e097acb3d104cc7b9ac7" + integrity sha512-3e0qYk87eczfzg5P73ZVuuxEGCBfatRhPze6KrSaIbEKVtmnFI1RYp1Fv+AyZi+w8kcNRSPeNX6ap4b65zEkiA== dependencies: - postcss "^7.0.6" + postcss "^8.2.7" postcss-selector-parser@^6.0.2: version "6.0.9" @@ -14553,31 +14030,14 @@ postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@^6.0.14: - version "6.0.23" - resolved "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz" - integrity sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag== +postcss@^7.0.36, postcss@^8.2.13, postcss@^8.2.7, postcss@^8.4.19, postcss@^8.4.31: + version "8.4.38" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e" + integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A== dependencies: - chalk "^2.4.1" - source-map "^0.6.1" - supports-color "^5.4.0" - -postcss@^7.0.0, postcss@^7.0.32, postcss@^7.0.6: - version "7.0.39" - resolved "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz" - integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA== - dependencies: - picocolors "^0.2.1" - source-map "^0.6.1" - -postcss@^8.4.19: - version "8.4.21" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4" - integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg== - dependencies: - nanoid "^3.3.4" + nanoid "^3.3.7" picocolors "^1.0.0" - source-map-js "^1.0.2" + source-map-js "^1.2.0" prelude-ls@^1.2.1: version "1.2.1" @@ -14708,11 +14168,6 @@ prop-types@15.8.1, prop-types@^15.0.0, prop-types@^15.5.0, prop-types@^15.5.10, object-assign "^4.1.1" react-is "^16.13.1" -propagate@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" - integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== - property-information@^6.0.0: version "6.1.1" resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.1.1.tgz#5ca85510a3019726cb9afed4197b7b8ac5926a22" @@ -15380,14 +14835,6 @@ regenerator-transform@^0.15.2: dependencies: "@babel/runtime" "^7.8.4" -regex-not@^1.0.0, regex-not@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" - integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== - dependencies: - extend-shallow "^3.0.2" - safe-regex "^1.1.0" - regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.3.1, regexp.prototype.flags@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" @@ -15522,16 +14969,6 @@ renderkid@^3.0.0: lodash "^4.17.21" strip-ansi "^6.0.1" -repeat-element@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" - integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== - -repeat-string@^1.6.1: - version "1.6.1" - resolved "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" - integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= - require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -15580,11 +15017,6 @@ resolve-dir@^1.0.0, resolve-dir@^1.0.1: expand-tilde "^2.0.0" global-modules "^1.0.0" -resolve-from@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz" - integrity sha1-six699nWiBvItuZTM17rywoYh0g= - resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" @@ -15600,17 +15032,12 @@ resolve-pathname@^3.0.0: resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== -resolve-url@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" - integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== - resolve.exports@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== -resolve@^1.10.0, resolve@^1.10.1, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.3.2, resolve@^1.4.0: +resolve@^1.10.0, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.3.2, resolve@^1.4.0: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== @@ -15619,6 +15046,15 @@ resolve@^1.10.0, resolve@^1.10.1, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.2 path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.17.0: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + resolve@^2.0.0-next.3: version "2.0.0-next.3" resolved "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz" @@ -15635,11 +15071,6 @@ restore-cursor@^3.1.0: onetime "^5.1.0" signal-exit "^3.0.2" -ret@~0.1.10: - version "0.1.15" - resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" - integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== - retry@^0.12.0: version "0.12.0" resolved "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz" @@ -15731,13 +15162,6 @@ safe-regex-test@^1.0.0: get-intrinsic "^1.1.3" is-regex "^1.1.4" -safe-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" - integrity sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg== - dependencies: - ret "~0.1.10" - "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.1.0: version "2.1.2" resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" @@ -15791,15 +15215,6 @@ schema-utils@2.7.0: ajv "^6.12.2" ajv-keywords "^3.4.1" -schema-utils@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" - integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g== - dependencies: - ajv "^6.1.0" - ajv-errors "^1.0.0" - ajv-keywords "^3.1.0" - schema-utils@^2.6.5: version "2.7.1" resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz" @@ -15886,13 +15301,14 @@ send@0.18.0: range-parser "~1.2.1" statuses "2.0.1" -sentence-case@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-2.1.1.tgz#1f6e2dda39c168bf92d13f86d4a918933f667ed4" - integrity sha512-ENl7cYHaK/Ktwk5OTD+aDbQ3uC8IByu/6Bkg+HDv8Mm+XnBnppVNalcfJTNsp1ibstKh030/JKQQWglDvtKwEQ== +sentence-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-3.0.4.tgz#3645a7b8c117c787fde8702056225bb62a45131f" + integrity sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg== dependencies: - no-case "^2.2.0" - upper-case-first "^1.1.2" + no-case "^3.0.4" + tslib "^2.0.3" + upper-case-first "^2.0.2" serialize-javascript@^6.0.0: version "6.0.0" @@ -15928,16 +15344,6 @@ set-cookie-parser@^2.4.6: resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.5.1.tgz#ddd3e9a566b0e8e0862aca974a6ac0e01349430b" integrity sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ== -set-value@^2.0.0, set-value@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" - integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" - setimmediate@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" @@ -16040,42 +15446,13 @@ smart-buffer@^4.2.0: resolved "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== -snake-case@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f" - integrity sha512-FMR5YoPFwOLuh4rRz92dywJjyKYZNLpMn1R5ujVpIYkbA9p01fq8RMg0FkO4M+Yobt4MjHeLTJVm5xFFBHSV2Q== +snake-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" + integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg== dependencies: - no-case "^2.2.0" - -snapdragon-node@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" - integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== - dependencies: - define-property "^1.0.0" - isobject "^3.0.0" - snapdragon-util "^3.0.1" - -snapdragon-util@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" - integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== - dependencies: - kind-of "^3.2.0" - -snapdragon@^0.8.1: - version "0.8.2" - resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" - integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== - dependencies: - base "^0.11.1" - debug "^2.2.0" - define-property "^0.2.5" - extend-shallow "^2.0.1" - map-cache "^0.2.2" - source-map "^0.5.6" - source-map-resolve "^0.5.0" - use "^3.1.0" + dot-case "^3.0.4" + tslib "^2.0.3" sockjs-client@1.6.1: version "1.6.1" @@ -16119,21 +15496,10 @@ source-list-map@^2.0.1: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== -source-map-js@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" - integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== - -source-map-resolve@^0.5.0: - version "0.5.3" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" - integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== - dependencies: - atob "^2.1.2" - decode-uri-component "^0.2.0" - resolve-url "^0.2.1" - source-map-url "^0.4.0" - urix "^0.1.0" +source-map-js@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" + integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== source-map-resolve@^0.6.0: version "0.6.0" @@ -16159,12 +15525,7 @@ source-map-support@^0.5.16, source-map-support@~0.5.20: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-url@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" - integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== - -source-map@^0.5.0, source-map@^0.5.6, source-map@^0.5.7: +source-map@^0.5.0, source-map@^0.5.7: version "0.5.7" resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== @@ -16174,7 +15535,7 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@^0.7.3, source-map@~0.7.2: +source-map@^0.7.3: version "0.7.4" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== @@ -16237,13 +15598,6 @@ spdx-license-ids@^3.0.0: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz#69077835abe2710b65f03969898b6637b505a779" integrity sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA== -split-string@^3.0.1, split-string@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" - integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== - dependencies: - extend-shallow "^3.0.0" - sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -16287,14 +15641,6 @@ stackframe@^1.3.4: resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== -static-extend@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" - integrity sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g== - dependencies: - define-property "^0.2.5" - object-copy "^0.1.0" - statuses@2.0.1, statuses@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" @@ -16451,7 +15797,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -16465,6 +15811,13 @@ strip-ansi@^3.0.0: dependencies: ansi-regex "^2.0.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -16521,7 +15874,7 @@ supports-color@^2.0.0: resolved "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= -supports-color@^5.3.0, supports-color@^5.4.0: +supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== @@ -16555,14 +15908,6 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -swap-case@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/swap-case/-/swap-case-1.1.2.tgz#c39203a4587385fad3c850a0bd1bcafa081974e3" - integrity sha512-BAmWG6/bx8syfc6qXPprof3Mn5vQgf5dwdUNJhsNqU9WdPt5P+ES/wQ5bxfijy8zwZgZZHslC3iAsxsuQMCzJQ== - dependencies: - lower-case "^1.1.1" - upper-case "^1.1.1" - swc-loader@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/swc-loader/-/swc-loader-0.2.3.tgz#6792f1c2e4c9ae9bf9b933b3e010210e270c186d" @@ -16718,13 +16063,13 @@ terser@^5.16.5: source-map-support "~0.5.20" terser@^5.7.2: - version "5.12.1" - resolved "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz" - integrity sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ== + version "5.14.2" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.2.tgz#9ac9f22b06994d736174f4091aa368db896f1c10" + integrity sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA== dependencies: + "@jridgewell/source-map" "^0.3.2" acorn "^8.5.0" commander "^2.20.0" - source-map "~0.7.2" source-map-support "~0.5.20" test-exclude@^6.0.0: @@ -16776,14 +16121,6 @@ tiny-warning@^1.0.0: resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== -title-case@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/title-case/-/title-case-2.1.1.tgz#3e127216da58d2bc5becf137ab91dae3a7cd8faa" - integrity sha512-EkJoZ2O3zdCz3zJsYCsxyq2OC5hrxR9mfdd5I+w8h/tmFfeOxJ+vvkxsKxdmN0WtS9zLdHEgfgVOiMVgv+Po4Q== - dependencies: - no-case "^2.2.0" - upper-case "^1.0.3" - tmatch@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/tmatch/-/tmatch-2.0.1.tgz#0c56246f33f30da1b8d3d72895abaf16660f38cf" @@ -16811,21 +16148,6 @@ to-fast-properties@^2.0.0: resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== -to-object-path@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" - integrity sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg== - dependencies: - kind-of "^3.0.2" - -to-regex-range@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" - integrity sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg== - dependencies: - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -16833,16 +16155,6 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -to-regex@^3.0.1, to-regex@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" - integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== - dependencies: - define-property "^2.0.2" - extend-shallow "^3.0.2" - regex-not "^1.0.2" - safe-regex "^1.1.0" - tocbot@^4.20.1: version "4.21.3" resolved "https://registry.yarnpkg.com/tocbot/-/tocbot-4.21.3.tgz#8172136dd0fc5233a74d32811c2efb2133542e26" @@ -17172,16 +16484,6 @@ unified@^10.0.0: trough "^2.0.0" vfile "^5.0.0" -union-value@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" - integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== - dependencies: - arr-union "^3.1.0" - get-value "^2.0.6" - is-extendable "^0.1.1" - set-value "^2.0.1" - unique-filename@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" @@ -17320,14 +16622,6 @@ unplugin@^1.3.1: webpack-sources "^3.2.3" webpack-virtual-modules "^0.5.0" -unset-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" - integrity sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ== - dependencies: - has-value "^0.3.1" - isobject "^3.0.0" - untildify@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz" @@ -17357,17 +16651,19 @@ update-browserslist-db@^1.0.9: escalade "^3.1.1" picocolors "^1.0.0" -upper-case-first@^1.1.0, upper-case-first@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-1.1.2.tgz#5d79bedcff14419518fd2edb0a0507c9b6859115" - integrity sha512-wINKYvI3Db8dtjikdAqoBbZoP6Q+PZUyfMR7pmwHzjC2quzSkUq5DmPrTtPEqHaz8AGtmsB4TqwapMTM1QAQOQ== +upper-case-first@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-2.0.2.tgz#992c3273f882abd19d1e02894cc147117f844324" + integrity sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg== dependencies: - upper-case "^1.1.1" + tslib "^2.0.3" -upper-case@^1.0.3, upper-case@^1.1.0, upper-case@^1.1.1, upper-case@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" - integrity sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA== +upper-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-2.0.2.tgz#d89810823faab1df1549b7d97a76f8662bae6f7a" + integrity sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg== + dependencies: + tslib "^2.0.3" uri-js@^4.2.2: version "4.4.1" @@ -17376,11 +16672,6 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -urix@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" - integrity sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg== - url-parse@^1.5.10, url-parse@^1.5.3: version "1.5.10" resolved "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz" @@ -17424,11 +16715,6 @@ use-sidecar@^1.1.2: detect-node-es "^1.1.0" tslib "^2.0.0" -use@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" - integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== - util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" From d6036b181bd92b96831988e33521a31887f87262 Mon Sep 17 00:00:00 2001 From: Eric Date: Fri, 5 Apr 2024 17:13:32 -0500 Subject: [PATCH 54/60] Schema: Remove unused and outdated `schema/fleet_schema.json` (#18092) Changes: - Deleted `schema/fleet_schema.json` (This file was previously used when merging the osquery schema with Fleet's overrides before we switched to using YAML override files). - Updated the description of the `generate-merged-schema` script. --- schema/fleet_schema.json | 1082 --------------------- website/scripts/generate-merged-schema.js | 2 +- 2 files changed, 1 insertion(+), 1083 deletions(-) delete mode 100644 schema/fleet_schema.json diff --git a/schema/fleet_schema.json b/schema/fleet_schema.json deleted file mode 100644 index 3930a691c5..0000000000 --- a/schema/fleet_schema.json +++ /dev/null @@ -1,1082 +0,0 @@ -[ - { - "name": "account_policy_data", - "examples": "Query the creation date of user accounts. You could also query the date of the last failed login attempt or password change.\n```\nSELECT strftime('%Y-%m-%d %H:%M:%S',creation_time,'unixepoch') AS creationdate FROM account_policy_data;\n```\n\nSee each user's last password set date and number of failed logins since last successful login to detect any intrusion attempts.\n```\nSELECT u.username u.uid, strftime('%Y-%m-%dT%H:%M:%S', a.password_last_set_time, 'unixepoch') AS password_last_set_time, a.failed_login_count, strftime('%Y-%m-%dT%H:%M:%S', a.failed_login_timestamp, 'unixepoch') AS failed_login_timestamp FROM account_policy_data AS a CROSS JOIN users AS u USING (uid) ORDER BY password_last_set_time ASC;\n```" - }, - { - "name": "ad_config", - "examples": "See the domain, if any, that the Mac is bound to.\n```\nSELECT domain FROM ad_config;\n```" - }, - { - "name": "alf", - "examples": "See the state of the Application Layer Firewall on a Mac. A result of 0 means it is disabled, 1 means it is enabled, and 2 means it is enabled and blocking all inbound connections. See our standard query library for an example policy query using this.\n```\nSELECT global_state FROM alf;\n```" - }, - { - "name": "alf_exceptions", - "examples": "List applications that are able to receive inbound connections across the firewall. This is useful when looking to see if vulnerable software is exposed to networks. \n```\nSELECT * FROM alf_exceptions;\n```" - }, - { - "name": "alf_explicit_auths", - "examples": "List applications were granted explicit access through the firewall. This is useful when looking to see if vulnerable software is exposed to networks. \n```\nSELECT * FROM alf_exceptions;\n```", - "notes": "This table is currently affected by a [bug](https://github.com/osquery/osquery/issues/2322) and not returning applications visible in the preferences interface." - }, - { - "name": "app_schemes", - "examples": "List applications that have registered the URL scheme \"mailto\" to handle email links.\n```\nSELECT * FROM app_schemes WHERE scheme='mailto';\n```" - }, - { - "name": "apps", - "examples": "See the last time applications were used. Useful to know if a vulnerable application is being used as well as for licensing purposes.\n```\nSELECT *, strftime('%Y-%m-%d %H:%M:%S',last_opened_time,'unixepoch') as LastUseDate FROM apps WHERE last_opened_time!='-1.0';\n```" - }, - { - "name": "arp_cache", - "examples": "List the content of the ARP cache.\n```\nSELECT address, interface, mac FROM arp_cache;\n```\nOn systems located in an office or datacenter, you can use this to watch for network attacks by checking for gateway IPs that do not have the expected MAC address. This could indicate an [ARP spoofing](https://en.wikipedia.org/wiki/ARP_spoofing) attack, in which an attacker that controls a system on the LAN attempts to funnel all remote traffic through it so they can inspect it.\n```\nSELECT * FROM arp_cache WHERE address IN (INSERT_GATEWAY_IPS) AND mac NOT IN (INSERT_EXPECTED_MAC_ADDRESSES);\n```\n", - "notes": "* The first six digits of a MAC address is the [Organizationally Unique Identifier (OUI)](https://en.wikipedia.org/wiki/Organizationally_unique_identifier).\n* You can lookup the manufacturer and model via the MAC address using a tool like [wireshark OUI lookup](https://www.wireshark.org/tools/oui-lookup.html)." - }, - { - "name": "asl", - "examples": "Apple System Logger (ASL) is deprecated since macOS 10.12. On older Macs, this table can be used to read logs. On newer ones, see the *unified_log* table. This example is from the osquery documentation.\n```\nSELECT time, message FROM asl WHERE facility = 'authpriv' AND sender = 'sudo' AND message LIKE '%python%';\n```" - }, - { - "name": "authorization_mechanisms", - "examples": "Discover privileged macOS authorization mechanisms, which could include third party software. Finding third party software using this means it is likely an important piece of software that should be kept very up to date.\n```\nSELECT * FROM authorization_mechanisms WHERE privileged='true';\n```" - }, - { - "name": "authorizations", - "examples": "See macOS authorizations that have been modified since their creation. Useful for threat hunting.\n```\nSELECT * FROM authorizations WHERE created!=modified;\n```" - }, - { - "name": "azure_instance_metadata", - "examples": "See in which Azure location a VM is located\n```\nSELECT location FROM azure_instance_metadata;\n```" - }, - { - "name": "azure_instance_tags", - "examples": "List the tags assigned to an Azure VM\n```\nSELECT key, value FROM azure_instance_tags;\n```" - }, - { - "name": "augeas", - "examples": "This table requires augeas [lenses](https://augeas.net/docs/lenses.html) to be installed in their default location. This query will output *sshd_config* as if it was a table. This is especially useful to check for specific configurations in text files.\n```\nSELECT * FROM augeas WHERE path='/etc/ssh/sshd_config';\n```" - }, - { - "name": "battery", - "examples": "This table contains a lot of information about the health of batteries. This query shows how many cycles the battery of a device was used for, allowing you to identify users who put more wear on it and might need more frequent replacements.\n```\nSELECT cycle_count FROM battery;\n```" - }, - { - "name": "browser_plugins", - "examples": "See classic browser plugins (C/NPAPI) installed by users. These plugins have been deprecated for a long time, so this query will usually not return anything.\n```\nSELECT bp.name, bp.identifier, bp.version FROM browser_plugins bp JOIN users u on bp.uid = u.uid ;\n```" - }, - { - "name": "curl", - "examples": "Connect over HTTP and retrieve statistics about the process. This is useful to detect machines on slow networks, or that have no Internet access.\n```\nSELECT round_trip_time FROM curl WHERE URL='https://fleetdm.com';\n```" - }, - { - "name": "curl_certificate", - "examples": "Identify the certificates being served to osquery clients. This can allow you to detect machines that are behind a proxy or firewall attempting to decrypt TLS, maliciously or not.\n```\nSELECT issuer_organization, signature, sha256_fingerprint FROM curl_certificate WHERE hostname='google.com';\n```" - }, - { - "name": "etc_hosts", - "examples": "Identify host\"name\"s pointed to IP addresses using the hosts file. This technique is often abused by malware, but can also indicate services that do not have proper DNS configuration to be reached from workstations.\n```\nSELECT * FROM etc_hosts WHERE address!='127.0.0.1' AND address!='::1' AND address!='255.255.255.255';\n```", - "columns": [ - { - "name": "pid_with_namespace", - "platforms": ["linux"] - } - ] - }, - { - "name": "block_devices", - "examples": "Identify USB storage in use\n```\nSELECT * FROM block_devices WHERE type='USB';\n```" - }, - { - "name": "carbon_black_info", - "examples": "See systems running Carbon Black but which have protection disabled.\n```\nSELECT * FROM carbon_black_info WHERE protection_disabled='1';\n```" - }, - { - "name": "certificates", - "description": "[Certificate authorities](https://en.wikipedia.org/wiki/Certificate_authority) installed in Keychains/ca-bundles.", - "examples": "Replace 1QAZ2WSX with your Apple Developer ID, if you have one. This query will then let you identify Macs that have a copy of your code signing and notarization certificates.\n```\nSELECT * FROM certificates WHERE common_\"name\" LIKE '%%1QAZ2SWX%%';\n```", - "columns": [ - { - "name": "sid", - "platforms": ["windows"] - }, - { - "name": "store_location", - "platforms": ["windows"] - }, - { - "name": "store", - "platforms": ["windows"] - }, - { - "name": "username", - "platforms": ["windows"] - }, - { - "name": "store_id", - "platforms": ["windows"] - }, - { - "name": "issuer2", - "platforms": ["linux", "darwin"] - }, - { - "name": "subject2", - "platforms": ["linux", "darwin"] - } - ] - }, - { - "name": "cpu_time", - "examples": "Identify overworked CPUs using a ratio of system to user CPU usage. Here, a ratio of 2 was arbitrarily chosen.\n```\nSELECT * FROM cpu_time WHERE user/system>2;\n```" - }, - { - "name": "cups_destinations", - "examples": "Identify the types of printers connected to computers. This query works for both network and local printers.\n```\nSELECT * FROM cups_destinations WHERE option_\"name\"='printer-info';\n```" - }, - { - "name": "cups_jobs", - "examples": "See what file format are being printed to what printer. This is useful for identifying systems that print a lot, which can help you ensure they have access to faster printers. Using this table, you could also highlight slow print jobs that might benefit from troubleshooting.\n```\nSELECT destination, format, strftime('%Y-%m-%d %H:%M:%S',creation_time,'unixepoch') AS creationDate FROM cups_jobs;\n```" - }, - { - "name": "cpuid", - "examples": "Identify Intel powered Macs that support a specific Intel CPU feature, such as sgx1.\n```\nSELECT * from cpuid WHERE feature='sgx1';\n```" - }, - { - "name": "device_firmware", - "examples": "Identify the firmware version of hardware on a Mac, such as the SSD controller in this case. Older versions might indicate a problem with software updates, and this information can be useful when troubleshooting various issues.\n```\nSELECT * FROM device_firmware WHERE device='AppleANS3NVMeController';\n```" - }, - { - "name": "disk_encryption", - "examples": "A policy query to check if Filevault disk encryption is enabled on a Mac.\n```\nSELECT 1 FROM disk_encryption WHERE user_uuid IS NOT '' AND filevault_status = 'on' LIMIT 1;\n```", - "columns": [ - { - "name": "uid", - "platforms": ["darwin"] - }, - { - "name": "user_uuid", - "platforms": ["darwin"] - }, - { - "name": "filevault_status", - "platforms": ["darwin"] - } - ] - }, - { - "name": "disk_events", - "examples": "This is an evented table, and as such, is more useful if you are sending osquery logs to a SIEM or other centralized destination via Fleet. Events must be enabled. This query will contain the list of all actions related to connecting and removing disks, including SMB drives and USB storage, which can be very useful for investigative purposes.\n```\nSELECT * FROM disk_events;\n```" - }, - { - "name": "docker_container_envs", - "examples": "This table allows you to list environment variables for running Docker containers. This query will output the value of a variable called *MYSQL_VERSION* for example.\n```\nSELECT key, value FROM docker_container_envs WHERE key LIKE 'MYSQL_VERSION';\n```" - }, - { - "name": "docker_container_labels", - "examples": "This table exposes all Docker labels on running containers. By joining it to the [docker_containers](https://fleetdm.com/tables/docker_containers)table, we can list containers and their maintainers.\n```\nSELECT dl.value, dc.name, FROM docker_container_labels dl JOIN docker_containers dc ON dl.id = dc.id WHERE key='maintainer';\n```" - }, - { - "name": "docker_container_mounts", - "examples": "List the source and destination of Docker bind and volume mounts.\n```\nSELECT dm.source, dm.destination, dm.mode, dc.name FROM docker_container_mounts dm JOIN docker_containers dc ON dm.id = dc.id;\n```" - }, - { - "name": "docker_container_networks", - "examples": "List the IP address of Docker containers.\n```\nSELECT dn.ip_address, dc.name FROM docker_container_networks dn JOIN docker_containers dc ON dn.id=dc.id;\n```" - }, - { - "name": "docker_container_ports", - "examples": "Identify the ports exposed at the host level and see which containers they redirect traffic to.\n```\nSELECT dc.name, dp.type, dp.port, dp.host_ip, dp.host_port FROM docker_container_ports dp JOIN docker_containers dc ON dp.id=dc.id WHERE dp.host_port !='0';\n```" - }, - { - "name": "event_taps", - "examples": "Identify processes that have a tap into the system, such as access to keystrokes, and view details on the executable including signature status, team identifier if signed and the authority that emitted the signing certificate. This can be used to detect keyloggers and other malicious applications.\n```\nSELECT t.event_tapped, s.identifier, s.signed, s.team_identifier, s.authority FROM event_taps t JOIN processes p ON p.pid = t.tapping_process JOIN signature s on s.path = p.path WHERE s.identifier !='com.apple.ViewBridgeAuxiliary' AND s.identifier !='com.apple.universalaccessd' AND s.identifier !='com.apple.accessibility.AXVisualSupportAgent';\n```" - }, - { - "name": "gatekeeper", - "examples": "Policy query to check that Gatekeeper is enabled\n```\nSELECT 1 FROM gatekeeper WHERE assessments_enabled = 1;\n```" - }, - { - "name": "homebrew_packages", - "examples": "Check the version of a package installed via homebrew. This example checks the version of ffmeg, which should be replaced by the actual package you want to check for. This is useful for finding problematic or vulnerable installs, though Fleet will detect vulnerable packages automatically.\n```\nSELECT version FROM homebrew_packages WHERE name = 'ffmpeg';\n```" - }, - { - "name": "groups", - "examples": "See all groups with the IsHidden OpenDirectory attribute\n```\nSELECT * FROM groups WHERE is_hidden='1';\n```", - "columns": [ - { - "name": "group_sid", - "platforms": ["windows"] - }, - { - "name": "comment", - "platforms": ["windows"] - }, - { - "name": "is_hidden", - "platforms": ["darwin"] - }, - { - "name": "pid_with_namespace", - "platforms": ["linux"] - } - ], - "notes": "* On Windows, `gid` and `gid_signed` are always the same" - }, - { - "name": "hash", - "examples": "List zip files in the downloads folder as well as their associated sha256 hash.\n```\nSELECT f.path, h.sha256 FROM file f JOIN hash h ON f.path = h.path WHERE f.path LIKE '/Users/%/Downloads/%%.zip';\n```", - "columns": [ - { - "name": "pid_with_namespace", - "platforms": ["linux"] - }, - { - "name": "mount_namespace_id", - "platforms": ["linux"] - } - ] - }, - { - "name": "interface_addresses", - "examples": "Find all interfaces that have a public Internet IP. This query filters out all RFC1918 IPv4 addresses as well as IPv6 localhost.\n```\nSELECT * FROM interface_addresses WHERE address NOT LIKE '192.168%%' AND address NOT LIKE '172.16%%' AND address NOT LIKE '172.17%%' AND address NOT LIKE '172.18%%' AND address NOT LIKE '172.19%%' AND address NOT LIKE '172.20%%' AND address NOT LIKE '172.21%%' AND address NOT LIKE '172.22%%' AND address NOT LIKE '172.23%%' AND address NOT LIKE '10.%%' AND address NOT LIKE '127.%%' AND address IS NOT NULL AND address IS NOT ' ' AND address IS NOT '' AND address IS NOT '::1' AND mask IS NOT 'ffff:ffff:ffff:ffff::';\n```", - "columns": [ - { - "name": "friendly_name", - "platforms": ["windows"] - } - ] - }, - { - "name": "interface_ipv6", - "examples": "Identify interfaces using IPv6 with forwarding enabled.\n```\nSELECT interface FROM interface_ipv6 WHERE forwarding_enabled='1';\n```" - }, - { - "name": "iokit_devicetree", - "examples": "List the components in a Mac's device tree\n```\nSELECT * from iokit_devicetree;\n```" - }, - { - "name": "iokit_registry", - "examples": "Identify devices with a Yubikey connected. The name will also contain the protocols supported by the key, such as FIDO.\n```\nSELECT * from iokit_registry WHERE name LIKE 'Yubi%';\n```" - }, - { - "name": "kernel_extensions", - "examples": "Identify third-party kernel extensions.\n```\nSELECT * FROM kernel_extensions WHERE name NOT LIKE 'com.apple%' AND name NOT LIKE '__kernel__';\n```" - }, - { - "name": "kernel_info", - "examples": "See the kernel version running\n```\nSELECT version FROM kernel_info;\n```" - }, - { - "name": "kernel_panics", - "examples": "Look for kernel panics and see which module was last loaded before they happened.\n```\nSELECT os_version, name, time, system_model, last_loaded FROM kernel_panics;\n```" - }, - { - "name": "keychain_acls", - "examples": "Identify keychain items with permissions granted to Applications at the system or user level.\n```\nSELECT * FROM keychain_acls WHERE path LIKE '/System/Applications/%%' OR path LIKE '/Users/%%/Applications/%%';\n```" - }, - { - "name": "keychain_items", - "columns": [{ "name": "created", "description": "Date item was created" }], - "examples": "Identify Macs that contain certificates related to Apple application signing and notarization. (replace with your Apple Developer ID string)\n```\nSELECT * FROM keychain_items WHERE label LIKE '%8EHZ83LZNU%';\n```" - }, - { - "name": "last", - "examples": "System logins and logouts with formatted time.\n```\nSELECT strftime('%Y-%m-%d %H:%M:%S',time,'unixepoch') AS formatted_time, username, pid, type FROM last WHERE tty='console'; \n```" - }, - { - "name": "launchd", - "examples": "List launch daemons that run an application in the Applications directory.\n```\nSELECT * FROM launchd WHERE program LIKE '/Applications/%%' OR program LIKE '/Users/%%/Applications/%%';\n```" - }, - { - "name": "managed_policies", - "examples": "Check if critical software update installation is enabled via a profile (1 = enabled)\n```\nSELECT name, value FROM managed_policies WHERE domain='com.apple.SoftwareUpdate' AND name='CriticalUpdateInstall' LIMIT 1;\n```" - }, - { - "name": "mdls", - "examples": "Identify hidden files that have been indexed by Spotlight. This could reveal files that were recently deleted and are still in the Spotlight database.\n```\nSELECT * FROM mdls WHERE path LIKE '/Users/g/%%' AND key='kMDItemFSIsExtensionHidden' AND value='true';\n```" - }, - { - "name": "nfs_shares", - "examples": "List shares exported via NFS on Macs, and if they are read only (readonly=1) or not.\n```\nSELECT share, readonly FROM nfs_shares;\n```" - }, - { - "name": "wifi_networks", - "examples": "Find WiFi networks configured on Macs that are unencrypted and require a captive portal. This can be useful to understand how much people use laptops in hotels, airports and other environments, and is a good indicator that tools such as DNS-over-HTTPS would improve privacy of connectivity.\n```\nSELECT network_name FROM wifi_networks WHERE security_type='Open' AND captive_portal='1';\n```" - }, - { - "name": "wifi_status", - "examples": "See the current speed of the WiFi connection, in megabits per second.\n```\nSELECT transmit_rate FROM wifi_status;\n```" - }, - { - "name": "wifi_survey", - "examples": "Count the amount of wireless networks visible to the computer.\n```\nSELECT COUNT ( DISTINCT network_name ) AS \"Number of wireless networks visible\" FROM wifi_survey;\n```" - }, - { - "name": "load_average", - "examples": "Find computers with a load average of 3.5 or higher over the last 15 minutes.\n```\nSELECT average from load_average WHERE period='15m' AND average>=3.5;\n```" - }, - { - "name": "location_services", - "examples": "If this query returns a 1 in the enabled column, location services are enabled on this Mac.\n```\nSELECT enabled from location_services;\n```" - }, - { - "name": "mounts", - "examples": "If this query returns a 1 in the enabled column, location services are enabled on this Mac.\n```\nSELECT enabled from location_services;\n```" - }, - { - "name": "nvram", - "examples": "If a Mac had a sleep failure, this query will return the reason for it.\n```\nSELECT name, value FROM nvram WHERE name='SleepWakeFailureString';\n```" - }, - { - "name": "osquery_events", - "examples": "Identify osquery event types which have no subscriber.\n```\nSELECT * from osquery_events WHERE subscriptions='0';\n```" - }, - { - "name": "osquery_extensions", - "examples": "Identify osquery extensions in use that are not part of osquery core.\n```\nSELECT name, path from osquery_extensions WHERE type IS NOT 'core';\n```" - }, - { - "name": "osquery_flags", - "examples": "If disable_events has a value of false, events are enabled.\n```\nSELECT description, name, value FROM osquery_flags WHERE name='disable_events';\n```" - }, - { - "name": "osquery_info", - "examples": "See the version of the currently running osquery.\n```\nSELECT version FROM osquery_info; \n```" - }, - { - "name": "osquery_packs", - "examples": "See query packs currently active on osquery.\n```\nSELECT name FROM osquery_packs WHERE active='1';\n```" - }, - { - "name": "osquery_registry", - "examples": "See the list of tables available on this instance of osquery.\n```\nSELECT DISTINCT name FROM osquery_registry;\n```" - }, - { - "name": "osquery_schedule", - "examples": "Identify scheduled queries that have been denylisted by the osquery watchdog. This could indicate queries that required a lot of resources to be executed. They will not be executed again until osquery restarts.\n```\nSELECT name, query FROM osquery_schedule WHERE denylisted='1';\n```" - }, - { - "name": "package_bom", - "examples": "List the bill of materials of a package. The receipts directory contains packages to installed applications.\n```\nSELECT * FROM package_bom WHERE path='/private/var/db/receipts/com.yubico.ykman.bom';\n```" - }, - { - "name": "package_install_history", - "examples": "See a list of packages installed in the last week.\n```\nSELECT name, version, source, datetime(time,'unixepoch') AS install_time from package_install_history WHERE install_time >= datetime('now','-7 days');\n```" - }, - { - "name": "package_receipts", - "examples": "List the location of receipt files related to installed packages.\n```\nSELECT * FROM package_receipts;\n```" - }, - { - "name": "platform_info", - "examples": "See version information about the boot system, such as iBoot on Apple Silicon\n```\nSELECT version FROM platform_info;\n```" - }, - { - "name": "plist", - "examples": "Read the contents of a plist file, formatted into a table\n```\nSELECT key, subkey, value FROM plist WHERE path LIKE '/Users/%%/Library/Preferences/com.apple.Terminal.plist';\n```" - }, - { - "name": "process_envs", - "examples": "See what PATH is configured as an environment variable.\n```\nSELECT DISTINCT value, key FROM process_envs WHERE key='PATH';\n```" - }, - { - "name": "process_memory_map", - "examples": "See the memory ranges with write permissions assigned to processes.\n```\nSELECT * FROM process_memory_map WHERE permissions LIKE '%w%';\n```" - }, - { - "name": "process_open_files", - "examples": "See what processes have which files open, for example, what processes are currently interacting with files with 1Password in their name?\n```\nSELECT f.path file_path, p.path process_path FROM process_open_files f JOIN processes p ON p.pid = f.pid WHERE f.path LIKE '%1Password%';\n```" - }, - { - "name": "running_apps", - "examples": "List all running applications. Filter on is_active='1' to see the application that currently has focus.\n```\nSELECT * FROM running_apps;\n```" - }, - { - "name": "secureboot", - "examples": "See the secure boot status (enabled or not) of Windows and Linux systems. You could create a policy looking for it to be set to 1.\n```\nSELECT secure_boot FROM secureboot;\n```" - }, - { - "name": "shared_folders", - "examples": "List all shared folders except for the standard public ones.\n```\nSELECT * FROM shared_folders WHERE path NOT LIKE '/Users/%%/Public%';\n```" - }, - { - "name": "sharing_preferences", - "examples": "Identify systems where any type of sharing is enabled. This table can be very useful for building policies for specific types of sharing.\n```\nSELECT * FROM sharing_preferences WHERE screen_sharing='1' OR file_sharing='1' OR printer_sharing='1' OR remote_login='1' OR remote_management='1' OR remote_apple_events='1' OR internet_sharing='1' OR bluetooth_sharing='1' OR disc_sharing='1' OR content_caching='1';\n```" - }, - { - "name": "signature", - "examples": "Identify system extensions that are not managed via MDM and see their signature status.\n```\nSELECT se.identifier, se.bundle_path, se.category, se.state, s.signed FROM system_extensions se JOIN signature s on s.path = se.bundle_path WHERE se.mdm_managed='0';\n```" - }, - { - "name": "sip_config", - "examples": "View the status of System Integrity Protection.\n```\nSELECT config_flag, enabled FROM sip_config WHERE config_flag='sip';\n```" - }, - { - "name": "smc_keys", - "examples": "See if the temperature sensor on an Intel Mac is returning values. SMC values aren't officially documented and as such this table is useful if you are troubleshooting and digging into a specific hardware related issue.\n```\nSELECT * FROM smc_keys WHERE key='TC0P';\n```" - }, - { - "name": "startup_items", - "examples": "List commands executed as user/logon startup items.\n```\nSELECT name, type FROM startup_items WHERE status='enabled';\n```" - }, - { - "name": "sudoers", - "examples": "Identify systems where sudo is configured in a way to allow users to retain their existing environment variables, which is a security risk.\n```\nSELECT header, source, rule_details FROM sudoers WHERE rule_details='!env_reset';\n```" - }, - { - "name": "system_extensions", - "examples": "Identify system extensions that are not managed via MDM and see their signature status.\n```\nSELECT se.identifier, se.bundle_path, se.category, se.state, s.signed FROM system_extensions se JOIN signature s on s.path = se.bundle_path WHERE se.mdm_managed='0';\n```" - }, - { - "name": "system_info", - "examples": "See the CPU architecture of a machine as well as who made it and what its serial number is.\n```\nSELECT CPU_type, hardware_vendor, hardware_model, hardware_serial FROM system_info;\n```" - }, - { - "name": "temperature_sensors", - "examples": "Identify systems with CPU temperature sensors above or equal to 90c.\n```\nSELECT name, celsius FROM temperature_sensors WHERE name LIKE 'CPU%' AND celsius>='90';\n```" - }, - { - "name": "time_machine_backups", - "examples": "See the time of the latest backup. In environments where you want to encourage backups, this can be useful to remind users to perform them, and in environments where you do not allow backups, to detect that they are happening.\n```\nSELECT strftime('%Y-%m-%d %H:%M:%S',backup_date,'unixepoch') AS last_backup FROM time_machine_backups;\n```" - }, - { - "name": "time_machine_destinations", - "examples": "If Time Machine is configured, see what destination it is configured to go to. \n```\nSELECT alias FROM time_machine_destinations;\n```" - }, - { - "name": "ulimit_info", - "examples": "Check the stack size limit\n```\nSELECT * FROM ulimit_info WHERE type='stack';\n```" - }, - { - "name": "uptime", - "examples": "See how long hosts that have been up for more than a month have been up. This could indicate systems that are not ephemeral as expected, or not being patched as frequently as they should be.\n```\nSELECT days FROM uptime WHERE days>='31'\n```" - }, - { - "name": "usb_devices", - "examples": "Identify Yubikeys currently connected. The model field contains information about what authentication protocols the keys are configured to support. This table can be used to track any type of USB device.\n```\nSELECT model, vendor, version FROM usb_devices WHERE vendor='Yubico';\n```" - }, - { - "name": "virtual_memory_info", - "examples": "Identify systems where memory swapping is occuring. These systems might benefit from more RAM.\n```\nSELECT * FROM virtual_memory_info WHERE swap_ins>'0';\n```" - }, - { - "name": "xprotect_entries", - "examples": "Identify the Bundlore variants Xprotect protects the computer from\n```\nSELECT * FROM xprotect_entries WHERE name LIKE 'OSX.Bundlore%';\n```" - }, - { - "name": "xprotect_meta", - "examples": "See the minimum version of specific components allowed by Xprotect. This usually means the previous versions have vulnerabilities that are being exploited at scale, or were exploited at scale at some point in time.\n```\nSELECT * FROM xprotect_meta WHERE min_version!='any';\n```" - }, - { - "name": "xprotect_reports", - "examples": "See all Xprotect activity reports, if any are present. This indicates potentially malicious software was blocked by Xprotect.\n```\nSELECT * FROM xprotect_reports;\n```" - }, - { - "name": "interface_details", - "columns": [ - { - "name": "link_speed", - "platforms": ["linux", "darwin"] - }, - { - "name": "pci_slot", - "platforms": ["linux"] - }, - { - "name": "friendly_name", - "platforms": ["windows"] - }, - { - "name": "description", - "platforms": ["windows"] - }, - { - "name": "manufacturer", - "platforms": ["windows"] - }, - { - "name": "connection_id", - "platforms": ["windows"] - }, - { - "name": "connection_status", - "platforms": ["windows"] - }, - { - "name": "enabled", - "platforms": ["windows"] - }, - { - "name": "physical_adapter", - "platforms": ["windows"] - }, - { - "name": "speed", - "platforms": ["windows"] - }, - { - "name": "service", - "platforms": ["windows"] - }, - { - "name": "dhcp_enabled", - "platforms": ["windows"] - }, - { - "name": "dhcp_lease_expires", - "platforms": ["windows"] - }, - { - "name": "dhcp_lease_obtained", - "platforms": ["windows"] - }, - { - "name": "dhcp_server", - "platforms": ["windows"] - }, - { - "name": "dns_domain", - "platforms": ["windows"] - }, - { - "name": "dns_domain_suffix_search_order", - "platforms": ["windows"] - }, - { - "name": "dns_host_name", - "platforms": ["windows"] - }, - { - "name": "dns_server_search_order", - "platforms": ["windows"] - } - ] - }, - { - "name": "apt_sources", - "examples": "On Ubuntu or other Debian based systems, identify APT repositories that are not maintained by Ubuntu.\n```\nSELECT * FROM apt_sources WHERE maintainer!='Ubuntu';\n```", - "columns": [ - { - "name": "pid_with_namespace", - "platforms": ["linux"] - } - ] - }, - { - "name": "deb_packages", - "columns": [ - { - "name": "pid_with_namespace", - "platforms": ["linux"] - }, - { - "name": "mount_namespace_id", - "platforms": ["linux"] - } - ] - }, - { - "name": "rpm_packages", - "columns": [ - { - "name": "pid_with_namespace", - "platforms": ["linux"] - }, - { - "name": "mount_namespace_id", - "platforms": ["linux"] - } - ] - }, - { - "name": "yum_sources", - "examples": "Find yum repositories on Linux servers for which cryptographic verification via GPG is disabled. This could allow untrusted packages to be injected into a repository that could then be installed.\n```\nSELECT * FROM yum_sources WHERE gpgcheck='0'; \n```", - "columns": [ - { - "name": "pid_with_namespace", - "platforms": ["linux"] - } - ] - }, - { - "name": "listening_ports", - "examples": "List executables listening on network ports.\n```\nSELECT l.port, l.pid, p.name, p.path FROM listening_ports l JOIN processes p USING (pid); \n```", - "columns": [ - { - "name": "net_namespace", - "platforms": ["linux"] - } - ] - }, - { - "name": "logged_in_users", - "examples": "See the user currently logged in on the console of the computer.\n```\nSELECT user, type, tty from logged_in_users WHERE tty='console';\n```", - "columns": [ - { - "name": "sid", - "platforms": ["windows"] - }, - { - "name": "registry_hive", - "platforms": ["windows"] - } - ] - }, - { - "name": "npm_packages", - "columns": [ - { - "name": "pid_with_namespace", - "platforms": ["linux"] - }, - { - "name": "mount_namespace_id", - "platforms": ["linux"] - } - ] - }, - { - "name": "os_version", - "examples": "See the OS version as well as the CPU architecture in use (X86 vs ARM for example)\n```\nSELECT arch, version FROM os_version;\n```", - "columns": [ - { - "name": "install_date", - "platforms": ["windows"] - }, - { - "name": "pid_with_namespace", - "platforms": ["linux"] - }, - { - "name": "mount_namespace_id", - "platforms": ["linux"] - } - ] - }, - { - "name": "authorized_keys", - "examples": "List the SSH keys allowed to connect to this host.\n```\nSELECT key FROM authorized_keys;\n```", - "columns": [ - { - "name": "pid_with_namespace", - "platforms": ["linux"] - }, - { - "name": "uid", - "requires_user_context": true - } - ] - }, - { - "name": "crontab", - "examples": "List commands scheduled for execution as cron jobs.\n```\nSELECT * FROM crontab;\n```", - "columns": [ - { - "name": "pid_with_namespace", - "platforms": ["windows"] - } - ] - }, - { - "name": "dns_resolvers", - "examples": "Identify computers that are using an external DNS server instead of an internal one. This query also removes null and empty strings that can be returned by this table.\n```\nSELECT address FROM dns_resolvers WHERE type='nameserver' AND address NOT LIKE '192.168%%' AND address NOT LIKE '172.16%%' AND address NOT LIKE '172.17%%' AND address NOT LIKE '172.18%%' AND address NOT LIKE '172.19%%' AND address NOT LIKE '172.20%%' AND address NOT LIKE '172.21%%' AND address NOT LIKE '172.22%%' AND address NOT LIKE '172.23%%' AND address NOT LIKE '10.%%' AND address NOT LIKE '127.%%' AND address IS NOT NULL AND address IS NOT ' ' AND address IS NOT ''; \n```", - "columns": [ - { - "name": "pid_with_namespace", - "platforms": ["linux"] - } - ] - }, - { - "name": "docker_containers", - "examples": "Identify containers that are running with high privileges.\n```\nSELECT state, status, image, image_id FROM docker_containers WHERE privileged='1';\n```", - "columns": [ - { - "name": "cgroup_namespace", - "platforms": ["linux"] - }, - { - "name": "ipc_namespace", - "platforms": ["linux"] - }, - { - "name": "mnt_namespace", - "platforms": ["linux"] - }, - { - "name": "net_namespace", - "platforms": ["linux"] - }, - { - "name": "pid_namespace", - "platforms": ["linux"] - }, - { - "name": "privileged", - "description": "Is the container [privileged](https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities)" - }, - { - "name": "user_namespace", - "platforms": ["linux"] - }, - { - "name": "uts_namespace", - "platforms": ["linux"] - } - ] - }, - { - "name": "docker_images", - "examples": "See how much storage is used by Docker images. Requires Docker to be running.\n```\nSELECT ROUND(SUM(size_bytes * 10e-10),2) as gigabytes_of_images FROM docker_images; \n```" - }, - { - "name": "docker_volumes", - "columns": [ - { - "name": "name", - "description": "Volume name from `docker volume ls`" - } - ] - }, - { - "name": "pci_devices", - "columns": [ - { - "name": "pci_class_id", - "platforms": ["linux"] - }, - { - "name": "pci_subclass_id", - "platforms": ["linux"] - }, - { - "name": "pci_subclass", - "platforms": ["linux"] - }, - { - "name": "subsystem_vendor_id", - "platforms": ["linux"] - }, - { - "name": "subsystem_vendor", - "platforms": ["linux"] - }, - { - "name": "subsystem_model_id", - "platforms": ["linux"] - }, - { - "name": "subsystem_model", - "platforms": ["linux"] - } - ] - }, - { - "name": "process_events", - "columns": [ - { - "name": "status", - "platforms": ["darwin"] - }, - { - "name": "fsuid", - "platforms": ["linux"] - }, - { - "name": "suid", - "platforms": ["linux"] - }, - { - "name": "fsgid", - "platforms": ["linux"] - }, - { - "name": "sgid", - "platforms": ["linux"] - }, - { - "name": "syscall", - "platforms": ["linux"] - } - ] - }, - { - "name": "suid_bin", - "examples": "Identify unsigned executables with suid privileges.\n```\nSELECT s.path, s.username, s.permissions, sig.signed, sig.team_identifier, sig.authority FROM suid_bin s JOIN signature sig on s.path = sig.path WHERE sig.signed='0';\n```", - "columns": [ - { - "name": "pid_with_namespace", - "platforms": ["linux"] - } - ] - }, - { - "name": "system_controls", - "examples": "See if IP forwarding is enabled (value=1) or not (current_value=0). This table provides access to a large quantity of low-level settings and is ideal to build policies.\n```\nSELECT current_value, name FROM system_controls WHERE name='net.inet.ip.forwarding';\n```", - "columns": [ - { - "name": "field_name", - "platforms": ["darwin"] - } - ] - }, - { - "name": "process_open_sockets", - "columns": [ - { - "name": "state", - "platforms": ["windows", "linux", "darwin"] - }, - { - "name": "net_namespace", - "platforms": ["linux"] - } - ] - }, - { - "name": "processes", - "examples": "List executables listening on network ports.\n```\nSELECT l.port, l.pid, p.name, p.path FROM listening_ports l JOIN processes p USING (pid); \n```", - "columns": [ - { - "name": "elevated_token", - "platforms": ["windows"] - }, - { - "name": "secure_process", - "platforms": ["windows"] - }, - { - "name": "protection_type", - "platforms": ["windows"] - }, - { - "name": "virtual_process", - "platforms": ["windows"] - }, - { - "name": "elapsed_time", - "platforms": ["windows"] - }, - { - "name": "handle_count", - "platforms": ["windows"] - }, - { - "name": "percent_processor_time", - "platforms": ["windows"] - }, - { - "name": "upid", - "platforms": ["darwin"] - }, - { - "name": "uppid", - "platforms": ["darwin"] - }, - { - "name": "cpu_type", - "platforms": ["darwin"] - }, - { - "name": "cpu_subtype", - "platforms": ["darwin"] - }, - { - "name": "translated", - "platforms": ["darwin"] - } - ] - }, - { - "name": "python_packages", - "examples": "List the versions of pip installed.\n```\nSELECT author, name, summary, version FROM python_packages WHERE name='pip';\n```", - "columns": [ - { - "name": "pid_with_namespace", - "platforms": ["linux"] - } - ] - }, - { - "name": "routes", - "examples": "Identify static routes\n```\nSELECT destination, interface, type FROM routes WHERE type='static';\n```", - "columns": [ - { - "name": "hopcount", - "platforms": ["linux", "darwin"] - } - ] - }, - { - "name": "user_ssh_keys", - "examples": "Identify SSH keys stored in clear text in user directories\n```\nSELECT * FROM users JOIN user_ssh_keys USING (uid) WHERE encrypted = 0;,\n```", - "columns": [ - { - "name": "pid_with_namespace", - "platforms": ["linux"] - }, - { - "name": "uid", - "requires_user_context": true - } - ] - }, - { - "name": "users", - "examples": "List users that have interactive access via a shell that isn't false.\n```\nSELECT * FROM users WHERE shell!='/usr/bin/false';\n```", - "columns": [ - { - "name": "type", - "platforms": ["windows"] - }, - { - "name": "is_hidden", - "platforms": ["darwin"] - }, - { - "name": "pid_with_namespace", - "platforms": ["linux"] - } - ] - }, - { - "name": "file", - "examples": "List zip files in the downloads folder as well as their associated sha256 hash.\n```\nSELECT f.path, h.sha256 FROM file f JOIN hash h ON f.path = h.path WHERE f.path LIKE '/Users/%/Downloads/%%.zip';\n```", - "columns": [ - { - "name": "attributes", - "platforms": ["windows"] - }, - { - "name": "volume_serial", - "platforms": ["windows"] - }, - { - "name": "file_id", - "platforms": ["windows"] - }, - { - "name": "file_version", - "platforms": ["windows"] - }, - { - "name": "product_version", - "platforms": ["windows"] - }, - { - "name": "original_filename", - "platforms": ["windows"] - }, - { - "name": "bsd_flags", - "platforms": ["darwin"] - }, - { - "name": "pid_with_namespace", - "platforms": ["linux"] - }, - { - "name": "mount_namespace_id", - "platforms": ["linux"] - } - ] - }, - { - "name": "time", - "examples": "View the timezone a system is configured in. \n```\nSELECT local_timezone FROM time;\n```", - "columns": [ - { - "name": "win_timestamp", - "platforms": ["windows"] - } - ] - }, - { - "name": "chrome_extension_content_scripts", - "columns": [ - { - "name": "uid", - "requires_user_context": true - } - ] - }, - { - "name": "chrome_extensions", - "examples": "List Chrome extensions by user and profile which have full access to HTTPS browsing.\n```\nSELECT u.username, ce.name, ce.description, ce.version, ce.profile, ce.permissions FROM users u CROSS JOIN chrome_extensions ce USING (uid) WHERE ce.permissions LIKE '%%https://*/*%%';\n```", - "columns": [ - { - "name": "uid", - "requires_user_context": true - } - ] - }, - { - "name": "crashes", - "examples": "See software responsible for crashes. This can be useful to detect what the most problematic software in your environment is.\n```\nSELECT crash_path, identifier, responsible, exception_type FROM crashes;\n```", - "columns": [ - { - "name": "uid", - "requires_user_context": true - } - ] - }, - { - "name": "preferences", - "examples": "This table reads a huge amount of preferences, including on third-party apps. This query will show how many users are enrolled to TouchID.\n```\nSELECT * FROM preferences WHERE subkey='dailyEvents/2/enrolledUserCount';\n```", - "columns": [ - { - "name": "username", - "requires_user_context": true - } - ] - }, - { - "name": "safari_extensions", - "columns": [ - { - "name": "uid", - "requires_user_context": true - } - ] - }, - { - "name": "firefox_addons", - "examples": "See Firefox extensions by user as well as information about their creator and automatic update status.\n```\nSELECT u.username, f.identifier, f.creator, f.description, f.version, f.autoupdate FROM users u CROSS JOIN firefox_addons f USING (uid) WHERE f.active='1';\n```", - "columns": [ - { - "name": "uid", - "requires_user_context": true - } - ] - }, - { - "name": "known_hosts", - "columns": [ - { - "name": "uid", - "requires_user_context": true - } - ] - }, - { - "name": "shell_history", - "examples": "See command line executions and related timestamps. Useful for threat hunting when a device is suspected of being compromised.\n```\nSELECT u.username, s.command, s.time FROM users u CROSS JOIN shell_history s USING (uid);\n```", - "columns": [ - { - "name": "uid", - "requires_user_context": true - } - ] - }, - { - "name": "ssh_configs", - "examples": "Identify SSH clients configured to send their locales to the server.\n```\nSELECT * FROM ssh_configs WHERE option='sendenv lang lc_*'; \n```", - "columns": [ - { - "name": "uid", - "requires_user_context": true - } - ] - } -] diff --git a/website/scripts/generate-merged-schema.js b/website/scripts/generate-merged-schema.js index 87f0a79abb..1fd98adf15 100644 --- a/website/scripts/generate-merged-schema.js +++ b/website/scripts/generate-merged-schema.js @@ -4,7 +4,7 @@ module.exports = { friendlyName: 'Generate merged schema', - description: 'Merge the osquery schema from the osquery/osquery-site GitHub repo with Fleet\'s overrides (/schema/fleet_schema.json) and save the merged schema to /schema/osquery_fleet_schema.json', + description: 'Merge the osquery schema from the osquery/osquery-site GitHub repo with Fleet\'s overrides (/schema/tables/) and save the merged schema to /schema/osquery_fleet_schema.json', From e21be6d27519708c53c63fa94cd132d5f1d24015 Mon Sep 17 00:00:00 2001 From: Eric Date: Fri, 5 Apr 2024 17:37:04 -0500 Subject: [PATCH 55/60] Handbook: fix duplicate ritual description in digital experience rituals (#18090) Changes: - Updated the description and moreInfoUrl for the "Check browser compatibility for fleetdm.com" ritual. --- handbook/digital-experience/digital-experience.rituals.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/handbook/digital-experience/digital-experience.rituals.yml b/handbook/digital-experience/digital-experience.rituals.yml index 0bd5a43e9a..072aa7cb11 100644 --- a/handbook/digital-experience/digital-experience.rituals.yml +++ b/handbook/digital-experience/digital-experience.rituals.yml @@ -5,8 +5,8 @@ task: "Check browser compatibility for fleetdm.com" startedOn: "2024-03-06" frequency: "Monthly" - description: "Run `npm audit --only=prod` to check for vulnerabilities on the production dependencies of fleetdm.com." - moreInfoUrl: "https://fleetdm.com/handbook/digital-experience#check-production-dependencies-of-fleetdm-com" + description: "Use Browserstack to manually QA pages on fleetdm.com in each of the earliest supported browser versions" + moreInfoUrl: "https://fleetdm.com/handbook/digital-experience#check-browser-compatibility-for-fleetdm-com" dri: "eashaw" autoIssue: # Enables automation of GitHub issues labels: [ "#g-digital-experience" ] # label to be applied to issue From e8487fae53c65b34e07f52919606f9d6383138e8 Mon Sep 17 00:00:00 2001 From: Victor Lyuboslavsky Date: Fri, 5 Apr 2024 17:43:10 -0500 Subject: [PATCH 56/60] In kubequery, updating golang.org/x/net dependency to fix GO-2024-2687 (#18108) #17903 In kubequery, updating golang.org/x/net dependency to fix GO-2024-2687 --- infrastructure/kubequery/go.mod | 8 ++++---- infrastructure/kubequery/go.sum | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/infrastructure/kubequery/go.mod b/infrastructure/kubequery/go.mod index c710815e69..f152612265 100644 --- a/infrastructure/kubequery/go.mod +++ b/infrastructure/kubequery/go.mod @@ -38,11 +38,11 @@ require ( github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/net v0.17.0 // indirect + golang.org/x/net v0.24.0 // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/term v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/term v0.19.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect golang.org/x/tools v0.6.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/infrastructure/kubequery/go.sum b/infrastructure/kubequery/go.sum index b423b68f8b..2876970659 100644 --- a/infrastructure/kubequery/go.sum +++ b/infrastructure/kubequery/go.sum @@ -381,6 +381,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -453,6 +455,7 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -461,6 +464,7 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -472,6 +476,7 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From f0e3259765c76f4c15083e4beae56bb736909642 Mon Sep 17 00:00:00 2001 From: Sam Pfluger <108141731+Sampfluger88@users.noreply.github.com> Date: Fri, 5 Apr 2024 17:46:35 -0500 Subject: [PATCH 57/60] Add CSE role (#17990) Co-authored-by: Jason Lewis <57552211+Patagonia121@users.noreply.github.com> --- handbook/company/open-positions.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/handbook/company/open-positions.yml b/handbook/company/open-positions.yml index 3e1682d8ea..fcc3a2e43b 100644 --- a/handbook/company/open-positions.yml +++ b/handbook/company/open-positions.yml @@ -61,3 +61,32 @@ - πŸ› οΈ Technical: You understand the software development processes. - 🟣 Openness: You are flexible and open to new ideas and ways of working - βž• Bonus: Cybersecurity or IT background +- jobTitle: πŸ‹ Customer Success Engineer + department: Customers + hiringManagerName: Jason Lewis + hiringManagerGithubUsername: Patagonia121 + hiringManagerLinkedInUrl: https://www.linkedin.com/in/jlewis0451/ + responsibilities: | + - 🎯 Strong attention to detail and can act as an encyclopedia of knowledge about how Fleet works - our customers represent a wide range of needs across many different use cases. Be adaptable to learning new things quickly and then share this knowledge with others. + - πŸ“£ Manage multiple customer deployments and escalations simultaneously with the ability to stay organized. + - πŸš€ Deploy Fleet on your own to have a better understanding of the customer experience and how the product works. + - πŸͺ΄ Promote product adoption, referencability, and customer advocacy with key customer stakeholders. + - πŸ₯‡ Be the first line of defense in customer Slack channels for any reported problems, how-to questions, feature request intake, and bug report filling. + - πŸš€ Work collaboratively with product and engineering teams to facilitate bug resolution and feature development based on customer asks. + - ⏫ Work hand-in-hand with the customer success team by participating in ad-hoc calls with customers to discuss any support issues they may have. + - πŸ’‘ Excellent communication and collaboration skills, with the ability to work closely with customer success, engineering, and product teams. + experience: | + - πŸ’­ Cybersecurity or IT background, experience with cloud environments like AWS and Azure or device management solutions like Fleet, Intune, Jamf Pro, Workspace One, etc. + - πŸ’– You know how to manage your time and priorities between customer support engagements, customer escalations, and other day-to-day responsibilities. + - 🧬 An excellent understanding of macOS, Windows, Linux and core services like Autopilot, ABM/ASM, MDM, ADE, APNs, syslog, etc. + - 🀝 Collaboration: You work best in a team-based environment. You are decisive with the ability to shift gears between thinking and doing. + - πŸ‘₯ A customer-centric mindset, focusing on delivering value and a positive user experience. + - πŸ¦‰ 2-3 years of work experience providing technical support to enterprise customers in the cybersecurity or device management space. Experience with executing and tracking results tied to customer escalations. + - πŸ› οΈ You are personable, enjoy being customer facing, and have a passion for problem solving while assisting external and internal stakeholders. + - πŸ§ͺ Extensive experience with Slack, Google Suite, and GitHub. + - ✍️ Familiarity with shell scripting, Python, Powershell, and using Terminal to execute commands or run scripts, and other line of business applications. + - 🟣 Openness: Speak freely. Interrupt and be interrupted. Give pointed and respectful feedback, even when you disagree. + - πŸ”΄ Empathy: You should demonstrate empathy by keenly understanding and addressing customer concerns with genuine compassion. + - βž• Bonus: Familiarity with osquery, MYSql, GitOps workflows, Terraform, Tines/Torq and open source projects. Experience working with IT, SRE, CPE, or SecOps teams. + + From 14c312361beacc79b1af5d0297ad2957b7fe477e Mon Sep 17 00:00:00 2001 From: Eric Date: Fri, 5 Apr 2024 19:09:58 -0500 Subject: [PATCH 58/60] Website: update `` component to conditionally disable animation (#17816) Closes: #17482 Changes: - Updated the `` component to display a static image if a user's browser has hardware/graphics acceleration disabled. --- .../js/components/parallax-city.component.js | 115 +++++++++++++----- .../components/parallax-city.component.less | 27 ++++ 2 files changed, 112 insertions(+), 30 deletions(-) diff --git a/website/assets/js/components/parallax-city.component.js b/website/assets/js/components/parallax-city.component.js index c2eee40749..6f3de558ef 100644 --- a/website/assets/js/components/parallax-city.component.js +++ b/website/assets/js/components/parallax-city.component.js @@ -1,7 +1,8 @@ /** * * ----------------------------------------------------------------------------- - * A button with a built-in loading spinner. + * An image of Fleet cloud city with a slight parallax scrolling effect. + * or a static image for mobile devices and browsers with hardware acceleration disabled. * * @type {Component} * @@ -27,6 +28,7 @@ parasails.registerComponent('parallaxCity', { distanceFromTopOfPage: undefined, // Used to check if the image is within the user's viewport. distanceFromBottomOfPage: undefined, // Used to track the amount of distance between the bottom of the image, and the bottom of the page. parallaxLayersAreCurrentlyAnimating: false, + enableAnimation: true,// Whether or not to disable the parallax scrolling animation. }; }, @@ -35,14 +37,16 @@ parasails.registerComponent('parallaxCity', { // β•© β•© β•© β•© ╩╩═╝ template: `
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
`, @@ -51,28 +55,20 @@ parasails.registerComponent('parallaxCity', { // β•‘ β•‘β• β•£ β•‘β•£ β•‘ β•šβ•¦β•β•‘ β•‘ β•‘β•£ // β•©β•β•β•©β•š β•šβ•β•β•šβ•β• β•© β•šβ•β•β•©β•β•β•šβ•β• beforeMount: function() { - + // Disable animation on mobile devices. + if(bowser.isMobile) { + this.enableAnimation = false; + } + // Check for hardware/graphics acceleration. + if(bowser.chrome || bowser.opera) { + this.enableAnimation = this.isHardwareAccelerationEnabledOnChromiumBrowsers(); + } else if(bowser.firefox){ + this.enableAnimation = this.isHardwareAccelerationEnabledOnFirefox(); + } }, mounted: async function(){ - if(!bowser.isMobile){ - // Store a reference to the parent container, we'll use this to determine the elements position relative to the user's viewport. - this.parallaxCityElement = $('div[purpose="parallax-city-container"]')[0]; - // Build an array of parallax layers, and set the initial bottom position of each layer to be negative the layer's scroll amount. - for(let layer of $('div.parallax-layer')) { - let scrollAmount = Number($(layer).attr('scroll-amount')); - $(layer).css('bottom', `-${scrollAmount}px`); - this.parallaxLayers.push({element: layer, scrollAmount}); - } - // Determine the parallax image's position on the page/user's viewport. - this.getElementPositions(); - // If the bottom of the element is within the user's viewport, update the positions of the layers. - if(this.parallaxCityElement.getBoundingClientRect().bottom > this.parallaxCityElement.offsetTop) { - this.scrollParallaxLayers(); - } - // Add a scroll event listener - $(window).scroll(this.throttleParallaxScroll); - // Add a resize event listener. - $(window).resize(this.getElementPositions); + if(!this.enableAnimation) { + this.setupParallaxAnimation(); } }, beforeDestroy: function() { @@ -89,6 +85,26 @@ parasails.registerComponent('parallaxCity', { this.distanceFromBottomOfPage = document.body.scrollHeight - this.distanceFromTopOfPage - (this.elementHeight * .5); this.elementBottomPosition = this.elementHeight + this.distanceFromTopOfPage; }, + setupParallaxAnimation: function() { + // Store a reference to the parent container, we'll use this to determine the elements position relative to the user's viewport. + this.parallaxCityElement = $('div[purpose="parallax-city-container"]')[0]; + // Build an array of parallax layers, and set the initial bottom position of each layer to be negative the layer's scroll amount. + for(let layer of $('div.parallax-layer')) { + let scrollAmount = Number($(layer).attr('scroll-amount')); + $(layer).css('bottom', `-${scrollAmount + 1}px`); + this.parallaxLayers.push({element: layer, scrollAmount}); + } + // Determine the parallax image's position on the page/user's viewport. + this.getElementPositions(); + // If the bottom of the element is within the user's viewport, update the positions of the layers. + if(this.parallaxCityElement.getBoundingClientRect().bottom > this.parallaxCityElement.offsetTop) { + this.scrollParallaxLayers(); + } + // Add a scroll event listener + $(window).scroll(this.throttleParallaxScroll); + // Add a resize event listener. + $(window).resize(this.getElementPositions); + }, scrollParallaxLayers: function() { if(!this.parallaxLayersAreCurrentlyAnimating) { this.parallaxLayersAreCurrentlyAnimating = true; @@ -111,6 +127,45 @@ parasails.registerComponent('parallaxCity', { setTimeout(()=>{ this.parallaxLayersAreCurrentlyAnimating = false; }, 100); - } + }, + isHardwareAccelerationEnabledOnChromiumBrowsers: function() { + let isHardwareAccelerationEnabled = true; + // For Chromium based browsers, we'll check the vendor of the user's graphics card. + // See https://gist.github.com/cvan/042b2448fcecefafbb6a91469484cdf8 for more info about this method. + let canvas = document.createElement('canvas'); + let webGLContext = canvas.getContext('webgl'); + if (!webGLContext) { + // If webGLContext is undefined, we'll assume the user has hardware acceleration disabled, and we won't animate the parallax layers. + isHardwareAccelerationEnabled = false; + } else { + // Otherwise, we'll check to see if the 'Vendor' of this users GPU. + let debugInfo = webGLContext.getExtension('WEBGL_debug_renderer_info'); + let vendor = webGLContext.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL); + // If vendor is "Google Inc. (Google)" or "Google Inc.", we can safely assume this user doesn't have hardware acceleration enabled and we'll disable the parallax animation. + if(vendor === 'Google Inc. (Google)' || vendor === 'Google Inc.') { + isHardwareAccelerationEnabled = false; + } + } + return isHardwareAccelerationEnabled; + }, + isHardwareAccelerationEnabledOnFirefox: function() { + // For Firefox, the method we use for chrome does not always work. + // Instead, we'll run two tests, one with forced software rendering, and one without to see if the results are the same. + // See https://stackoverflow.com/a/77170999 for more info about this method. + let canvas = document.createElement('canvas'); + let ctx = canvas.getContext('2d', { willReadFrequently: false }); + ctx.moveTo(0, 0); + ctx.lineTo(120, 121); + ctx.stroke(); + let firstTestResults = ctx.getImageData(0, 0, 200, 200).data.join(); + let canvasForSoftwareRenderingTest = document.createElement('canvas'); + let ctxWithSoftwareRendering = canvasForSoftwareRenderingTest.getContext('2d', { willReadFrequently: true });// willReadFrequently will force software rendering + ctxWithSoftwareRendering.moveTo(0, 0); + ctxWithSoftwareRendering.lineTo(120, 121); // HWA is bad at obliques + ctxWithSoftwareRendering.stroke(); + let softwareRenderingTestResults = ctxWithSoftwareRendering.getImageData(0, 0, 200, 200).data.join(); + // If the results from the software rendering test are identical to the first test, we can assume the user has hardware acceleration disabled. + return firstTestResults !== softwareRenderingTestResults; + }, } }); diff --git a/website/assets/styles/components/parallax-city.component.less b/website/assets/styles/components/parallax-city.component.less index 922db2d3f2..12f51655df 100644 --- a/website/assets/styles/components/parallax-city.component.less +++ b/website/assets/styles/components/parallax-city.component.less @@ -14,6 +14,14 @@ display: flex; flex-direction: column; justify-content: flex-end; + [purpose='static-cloud-city'] { + background-image: url('/images/parallax-cloud-city/cloud-city-static-7050x600@2x.png'); + background-size: cover; + background-position: center bottom; + background-repeat: no-repeat; + height: 456px; + position: relative; + } [purpose='parallax-city-container'] { height: 456px; position: relative; @@ -75,11 +83,17 @@ [purpose='parallax-city-container'] { height: 400px; } + [purpose='static-cloud-city'] { + height: 400px; + } } @media (max-width: 768px) { [purpose='parallax-city-container'] { height: 300px; } + [purpose='static-cloud-city'] { + height: 300px; + } } @media (max-width: 575px) { [purpose='parallax-city-container'] { @@ -92,10 +106,23 @@ display: none; } } + [purpose='static-cloud-city'] { + height: 300px; + background-image: url('/images/parallax-cloud-city/cloud-city-static-576x300@2x.png'); + background-size: cover; + background-position: center bottom; + background-repeat: no-repeat; + .parallax-layer { + display: none; + } + } } @media (max-width: 375px) { [purpose='parallax-city-container'] { height: 200px; } + [purpose='static-cloud-city'] { + height: 200px; + } } } From a389822f421c7f0c7c3017a093e43fa2b2c16cff Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 8 Apr 2024 05:39:19 -0500 Subject: [PATCH 59/60] Website: Add steps to `/start` questionnaire (#18074) Closes: #18047 Changes: - Added three steps to the /start questionnaire - Updated the contact page to prefill information for logged-in users by default - Updated the layout of the f/leetctl-preview page for users navigating to it from the /start page. - Updated the quote for vulnerability management on the /start and /contact pages to have a logo (There will be a separate PR, to add it to testimonials.yml) --------- Co-authored-by: Mike Thomas <78363703+mike-j-thomas@users.noreply.github.com> --- .../save-questionnaire-progress.js | 24 ++- website/api/controllers/view-contact.js | 16 +- .../api/controllers/view-fleetctl-preview.js | 11 +- website/api/models/User.js | 3 +- .../images/logo-rivian-dark-120x32@2x.png | Bin 0 -> 2847 bytes website/assets/js/pages/start.page.js | 53 +++++- website/assets/styles/pages/contact.less | 1 - .../assets/styles/pages/fleetctl-preview.less | 5 + website/assets/styles/pages/start.less | 14 +- website/views/pages/contact.ejs | 1 + website/views/pages/fleetctl-preview.ejs | 23 ++- website/views/pages/start.ejs | 161 +++++++++++++++++- 12 files changed, 269 insertions(+), 43 deletions(-) create mode 100644 website/assets/images/logo-rivian-dark-120x32@2x.png diff --git a/website/api/controllers/save-questionnaire-progress.js b/website/api/controllers/save-questionnaire-progress.js index b2559c4ee3..8ef697de53 100644 --- a/website/api/controllers/save-questionnaire-progress.js +++ b/website/api/controllers/save-questionnaire-progress.js @@ -18,6 +18,9 @@ module.exports = { 'how-many-hosts', 'will-you-be-self-hosting', 'what-are-you-working-on-eo-security', + 'what-does-your-team-manage-eo-it', + 'what-does-your-team-manage-vm', + 'what-do-you-manage-mdm', 'is-it-any-good', 'what-did-you-think', 'deploy-fleet-in-your-environment', @@ -28,21 +31,21 @@ module.exports = { formData: { type: {}, description: 'The formdata that will be saved for this step of the get started questionnaire' - } + }, }, exits: { - + success: { + outputDescription: 'All get started questionnaire answers accumulated so far by this user.', + outputType: {} + }, }, fn: async function ({currentStep, formData}) { // find this user's DB record. - let userRecord = await User.findOne({id: this.req.me.id}); - if(!userRecord){ - throw new Error(`Consistency violation: when trying to save a user's progress in the get started questionnaire, a User record with the ID ${this.req.me.id} could not be found.`); - } + let userRecord = this.req.me; let questionnaireProgress; // If this user doesn't have a lastSubmittedGetStartedQuestionnaireStep or getStartedQuestionnaireAnswers, create an empty dictionary to store their answers. if(!userRecord.lastSubmittedGetStartedQuestionnaireStep || _.isEmpty(userRecord.getStartedQuestionnaireAnswers)) { @@ -53,7 +56,9 @@ module.exports = { // When the 'what-are-you-using-fleet-for' is completed, update this user's DB record and session to include their answer. if(currentStep === 'what-are-you-using-fleet-for') { let primaryBuyingSituation = formData.primaryBuyingSituation; - await User.updateOne({id: this.req.me.id}).set({primaryBuyingSituation}); + await User.updateOne({id: this.req.me.id}).set({ + primaryBuyingSituation + }); // Set the primary buying situation in the user's session. this.req.session.primaryBuyingSituation = primaryBuyingSituation; } @@ -62,7 +67,10 @@ module.exports = { // Clone the questionnaireProgress to prevent any mutations from sending it through the updateOne Waterline method. let getStartedProgress = _.clone(questionnaireProgress); // Update the user's database model. - await User.updateOne({id: userRecord.id}).set({getStartedQuestionnaireAnswers: questionnaireProgress, lastSubmittedGetStartedQuestionnaireStep: currentStep}); + await User.updateOne({id: userRecord.id}).set({ + getStartedQuestionnaireAnswers: questionnaireProgress, + lastSubmittedGetStartedQuestionnaireStep: currentStep + }); // Return the JSON dictionary of form data submitted by this user. return getStartedProgress; } diff --git a/website/api/controllers/view-contact.js b/website/api/controllers/view-contact.js index 8281db2fb2..6951bdbfc1 100644 --- a/website/api/controllers/view-contact.js +++ b/website/api/controllers/view-contact.js @@ -12,11 +12,6 @@ module.exports = { description: 'A boolean that determines whether or not to display the talk to us form when the contact page loads.', defaultsTo: false, }, - - prefillFormDataFromUserRecord: { - type: 'boolean', - description: 'If true, the contact form will be prefilled in with information from this user\'s account.', - }, }, exits: { @@ -28,19 +23,18 @@ module.exports = { }, - fn: async function ({sendMessage, prefillFormDataFromUserRecord}) { + fn: async function ({sendMessage}) { let formToShow = 'talk-to-us'; if(sendMessage) { formToShow = 'contact'; } - // If the prefillFormDataFromUserRecord flag was set to true, but this user is not logged in, set it to false. - if(prefillFormDataFromUserRecord && !this.req.me){ - prefillFormDataFromUserRecord = false; - } // Respond with view. - return {formToShow, prefillFormDataFromUserRecord}; + return { + formToShow, + prefillFormDataFromUserRecord: this.req.me ? true : false// FUTURE: move to frontend. + }; } diff --git a/website/api/controllers/view-fleetctl-preview.js b/website/api/controllers/view-fleetctl-preview.js index 4c111c5fb8..0712b57acb 100644 --- a/website/api/controllers/view-fleetctl-preview.js +++ b/website/api/controllers/view-fleetctl-preview.js @@ -6,6 +6,13 @@ module.exports = { description: 'Display "fleetctl preview" page.', + inputs: { + start: { + type: 'boolean', + description: 'A boolean flag that will hide the "next steps" buttons on the page if set to true', + defaultsTo: false, + } + }, exits: { @@ -21,10 +28,10 @@ module.exports = { }, - fn: async function () { + fn: async function ({start}) { // Respond with view. - return {}; + return {hideNextStepsButtons: start}; } diff --git a/website/api/models/User.js b/website/api/models/User.js index f664e0420d..db66de3d38 100644 --- a/website/api/models/User.js +++ b/website/api/models/User.js @@ -216,7 +216,8 @@ without necessarily having a billing card.` lastSubmittedGetStartedQuestionnaireStep: { type: 'string', - description: 'The last step the user reached in the get started form.' + description: 'The last step the user reached in the get started form.', + defaultsTo: 'start', }, getStartedQuestionnaireAnswers: { diff --git a/website/assets/images/logo-rivian-dark-120x32@2x.png b/website/assets/images/logo-rivian-dark-120x32@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..8e11b7ad5c839fc22068c710685c2c9b4e2f6cda GIT binary patch literal 2847 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
+
Rivian logo

The visibility down into the assets covered by the agent is phenomenal. Fleet has become the central source for a lot of things.

diff --git a/website/views/pages/fleetctl-preview.ejs b/website/views/pages/fleetctl-preview.ejs index d932a47d51..09c2f86364 100644 --- a/website/views/pages/fleetctl-preview.ejs +++ b/website/views/pages/fleetctl-preview.ejs @@ -102,15 +102,22 @@
-

Next steps

-
+<%- /* Expose server-rendered data as window.SAILS_LOCALS :: */ exposeLocalsToBrowser() %> diff --git a/website/views/pages/start.ejs b/website/views/pages/start.ejs index ea6dad2f8d..0b5b407a3f 100644 --- a/website/views/pages/start.ejs +++ b/website/views/pages/start.ejs @@ -30,12 +30,12 @@
-

How many hosts do you have:

+
+

About how many hosts do you have?

+

Including servers, containers, workstations, and other hosts.

+

Including macOS, Windows, Linux workstations, Chromebooks, servers, and other hosts.

+

Including computers, servers, OT/ICS, containers, and other hosts.

+
@@ -231,6 +236,144 @@
+ <%// ┬ ┬┬ β”¬β”Œβ”€β”β”Œβ”¬β” β”Œβ”¬β”β”Œβ”€β”β”Œβ”€β”β”Œβ”€β” ┬ β”¬β”Œβ”€β”β”¬ ┬┬─┐ β”Œβ”¬β”β”Œβ”€β”β”Œβ”€β”β”Œβ”¬β” β”Œβ”¬β”β”Œβ”€β”β”Œβ”β”Œβ”Œβ”€β”β”Œβ”€β”β”Œβ”€β” + // β”‚β”‚β”‚β”œβ”€β”€β”œβ”€β”€ β”‚ β”‚β”‚β”‚ β”‚β”œβ”€ └─┐ β””β”¬β”˜β”‚ β”‚β”‚ β”‚β”œβ”¬β”˜ β”‚ β”œβ”€ β”œβ”€β”€β”‚β”‚β”‚ β”‚β”‚β”‚β”œβ”€β”€β”‚β”‚β”‚β”œβ”€β”€β”‚ β”¬β”œβ”€ + // β””β”΄β”˜β”΄ β”΄β”΄ β”΄ β”΄ β”€β”΄β”˜β””β”€β”˜β””β”€β”˜β””β”€β”˜ β”΄ β””β”€β”˜β””β”€β”˜β”΄β””β”€ β”΄ β””β”€β”˜β”΄ β”΄β”΄ β”΄ β”΄ β”΄β”΄ β”΄β”˜β””β”˜β”΄ β”΄β””β”€β”˜β””β”€β”˜ + // β”Œβ”€β”β”Œβ”€β” β”¬β”Œβ”¬β” + // β”œβ”€ β”‚ │───│ β”‚ + // β””β”€β”˜β””β”€β”˜ β”΄ β”΄%> +
+
+
+ πŸ† +
+

What does your team manage?

+ +
+ +
+ + + + +
Please select an option
+
+
+ +
+ Back + Continue +
+
+
+ <%// ┬ ┬┬ β”¬β”Œβ”€β”β”Œβ”¬β” β”Œβ”¬β”β”Œβ”€β”β”Œβ”€β”β”Œβ”€β” ┬ β”¬β”Œβ”€β”β”¬ ┬┬─┐ β”Œβ”¬β”β”Œβ”€β”β”Œβ”€β”β”Œβ”¬β” β”Œβ”¬β”β”Œβ”€β”β”Œβ”β”Œβ”Œβ”€β”β”Œβ”€β”β”Œβ”€β” + // β”‚β”‚β”‚β”œβ”€β”€β”œβ”€β”€ β”‚ β”‚β”‚β”‚ β”‚β”œβ”€ └─┐ β””β”¬β”˜β”‚ β”‚β”‚ β”‚β”œβ”¬β”˜ β”‚ β”œβ”€ β”œβ”€β”€β”‚β”‚β”‚ β”‚β”‚β”‚β”œβ”€β”€β”‚β”‚β”‚β”œβ”€β”€β”‚ β”¬β”œβ”€ + // β””β”΄β”˜β”΄ β”΄β”΄ β”΄ β”΄ β”€β”΄β”˜β””β”€β”˜β””β”€β”˜β””β”€β”˜ β”΄ β””β”€β”˜β””β”€β”˜β”΄β””β”€ β”΄ β””β”€β”˜β”΄ β”΄β”΄ β”΄ β”΄ β”΄β”΄ β”΄β”˜β””β”˜β”΄ β”΄β””β”€β”˜β””β”€β”˜ + // ┬ β”¬β”Œβ”¬β” + // β””β”β”Œβ”˜β”‚β”‚β”‚ + // β””β”˜ β”΄ β”΄%> +
+
+
+ πŸ† +
+

What does your team manage?

+ +
+ +
+ + + + + +
Please select an option
+
+
+ +
+ Back + Continue +
+
+
+ <%// ┬ ┬┬ β”¬β”Œβ”€β”β”Œβ”¬β” β”Œβ”¬β”β”Œβ”€β” ┬ β”¬β”Œβ”€β”β”¬ ┬ β”Œβ”¬β”β”Œβ”€β”β”Œβ”β”Œβ”Œβ”€β”β”Œβ”€β”β”Œβ”€β” + // β”‚β”‚β”‚β”œβ”€β”€β”œβ”€β”€ β”‚ β”‚β”‚β”‚ β”‚ β””β”¬β”˜β”‚ β”‚β”‚ β”‚ β”‚β”‚β”‚β”œβ”€β”€β”‚β”‚β”‚β”œβ”€β”€β”‚ β”¬β”œβ”€ + // β””β”΄β”˜β”΄ β”΄β”΄ β”΄ β”΄ β”€β”΄β”˜β””β”€β”˜ β”΄ β””β”€β”˜β””β”€β”˜ β”΄ β”΄β”΄ β”΄β”˜β””β”˜β”΄ β”΄β””β”€β”˜β””β”€β”˜ + // β”Œβ”¬β”β”Œβ”¬β”β”Œβ”¬β” + // β”‚β”‚β”‚ β”‚β”‚β”‚β”‚β”‚ + // β”΄ β”΄β”€β”΄β”˜β”΄ β”΄%> +
+
+
+ πŸ† +
+

What do you manage?

+ +
+ +
+ + + +
Please select an option
+
+
+ +
+ Back + Continue +
+
+
<%// β”¬β”Œβ”€β” β”¬β”Œβ”¬β” β”Œβ”€β”β”Œβ”β”Œβ”¬ ┬ β”Œβ”€β”β”Œβ”€β”β”Œβ”€β”β”Œβ”¬β” // │└─┐ β”‚ β”‚ β”œβ”€β”€β”‚β”‚β”‚β””β”¬β”˜ β”‚ ┬│ β”‚β”‚ β”‚ β”‚β”‚ // β”΄β””β”€β”˜ β”΄ β”΄ β”΄ β”΄β”˜β””β”˜ β”΄ β””β”€β”˜β””β”€β”˜β””β”€β”˜β”€β”΄β”˜%> @@ -240,12 +383,13 @@
πŸ† -
-

Is it any good?

-

We don’t have a public sample environment, but you can try Fleet out locally on your computer.

+

Is it any good?

+
+

We don’t have a public sample environment, but you can try Fleet out locally on your computer.

+

Got questions? Ask us anything.

+
Rivian logo

The visibility down into the assets covered by the agent is phenomenal. Fleet has become the central source for a lot of things.

@@ -496,7 +641,7 @@

Spin up a local demo or get your Fleet Premium license key.

- + Run a local demo of Fleet

Try Fleet

Run a local demo of Fleet

From f1b45a386589c6be1a8309a38932608465096427 Mon Sep 17 00:00:00 2001 From: Roberto Dip Date: Mon, 8 Apr 2024 08:42:42 -0300 Subject: [PATCH 60/60] friday tidy up party (#18106) - add missing tests - remove unused code - remove unnecessary nested branches --- cmd/fleetctl/get_test.go | 4 +- ee/fleetctl/updates.go | 4 +- ee/tools/mdm/certs_test.go | 3 +- server/mdm/apple/util.go | 26 --- .../mdm/internal/commonmdm/commonmdm_test.go | 67 ++++++ server/mdm/microsoft/microsoft_mdm.go | 4 - server/mdm/microsoft/wstep_csr_test.go | 201 ++++++++++++++++++ server/vulnerabilities/utils/rpmvercmp.go | 14 +- 8 files changed, 281 insertions(+), 42 deletions(-) create mode 100644 server/mdm/internal/commonmdm/commonmdm_test.go create mode 100644 server/mdm/microsoft/wstep_csr_test.go diff --git a/cmd/fleetctl/get_test.go b/cmd/fleetctl/get_test.go index b770565a8e..cd54fc3ed9 100644 --- a/cmd/fleetctl/get_test.go +++ b/cmd/fleetctl/get_test.go @@ -1457,9 +1457,9 @@ func TestGetQuery(t *testing.T) { Platform: "linux", Logging: "differential", }, nil - } else { - return nil, ¬FoundError{} } + + return nil, ¬FoundError{} } expectedYaml := `--- diff --git a/ee/fleetctl/updates.go b/ee/fleetctl/updates.go index 371f09041e..09010e62ee 100644 --- a/ee/fleetctl/updates.go +++ b/ee/fleetctl/updates.go @@ -764,8 +764,8 @@ func (p *passphraseHandler) checkPassphrase(store tuf.LocalStore, role string) e continue } else if len(keys) == 0 { return fmt.Errorf("%s key not found", role) - } else { - return nil } + + return nil } } diff --git a/ee/tools/mdm/certs_test.go b/ee/tools/mdm/certs_test.go index b8f57fef4b..8d42574f65 100644 --- a/ee/tools/mdm/certs_test.go +++ b/ee/tools/mdm/certs_test.go @@ -11,7 +11,8 @@ const ( // These are just example keys generated locally with openssl. They are intentionally published // and should never be used in production. exampleKeyPassphrase = "password" - exampleKeyPEM = `-----BEGIN RSA PRIVATE KEY----- + // #nosec G101 + exampleKeyPEM = `-----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: DES-EDE3-CBC,5F4D77F29A9E2675 diff --git a/server/mdm/apple/util.go b/server/mdm/apple/util.go index bc8b2bc6e4..40b669da93 100644 --- a/server/mdm/apple/util.go +++ b/server/mdm/apple/util.go @@ -7,7 +7,6 @@ import ( "crypto/x509" "encoding/binary" "encoding/pem" - "errors" "fmt" "math" "net/url" @@ -36,18 +35,6 @@ func EncodeCertPEM(cert *x509.Certificate) []byte { return pem.EncodeToMemory(&block) } -func DecodeCertPEM(encoded []byte) (*x509.Certificate, error) { - block, _ := pem.Decode(encoded) - if block == nil { - return nil, errors.New("no PEM-encoded data found") - } - if block.Type != "CERTIFICATE" { - return nil, fmt.Errorf("unexpected block type %s", block.Type) - } - - return x509.ParseCertificate(block.Bytes) -} - func EncodeCertRequestPEM(cert *x509.CertificateRequest) []byte { pemBlock := &pem.Block{ Type: "CERTIFICATE REQUEST", @@ -67,19 +54,6 @@ func EncodePrivateKeyPEM(key *rsa.PrivateKey) []byte { return pem.EncodeToMemory(&block) } -// DecodePrivateKeyPEM decodes PEM-encoded private key data. -func DecodePrivateKeyPEM(encoded []byte) (*rsa.PrivateKey, error) { - block, _ := pem.Decode(encoded) - if block == nil { - return nil, errors.New("no PEM-encoded data found") - } - if block.Type != "RSA PRIVATE KEY" { - return nil, fmt.Errorf("unexpected block type %s", block.Type) - } - - return x509.ParsePKCS1PrivateKey(block.Bytes) -} - // GenerateRandomPin generates a `lenght`-digit PIN number that takes into // account the current time as described in rfc4226 (for one time passwords) // diff --git a/server/mdm/internal/commonmdm/commonmdm_test.go b/server/mdm/internal/commonmdm/commonmdm_test.go new file mode 100644 index 0000000000..8423983163 --- /dev/null +++ b/server/mdm/internal/commonmdm/commonmdm_test.go @@ -0,0 +1,67 @@ +package commonmdm + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestResolveURL(t *testing.T) { + type testCase struct { + serverURL string + relPath string + cleanQuery bool + expected string + expectErr bool + } + + testCases := []testCase{ + { + serverURL: "http://example.com", + relPath: "path/to/resource", + cleanQuery: false, + expected: "http://example.com/path/to/resource", + expectErr: false, + }, + { + serverURL: "http://example.com?query=string", + relPath: "path", + cleanQuery: true, + expected: "http://example.com/path", + expectErr: false, + }, + { + serverURL: "http://example.com/base/", + relPath: "/path", + cleanQuery: false, + expected: "http://example.com/base/path", + expectErr: false, + }, + { + serverURL: "http://example.com", + relPath: "path/to/resource", + cleanQuery: true, + expected: "http://example.com/path/to/resource", + expectErr: false, + }, + { + serverURL: ":invalidurl", + relPath: "path", + cleanQuery: false, + expected: "", + expectErr: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.serverURL+"_"+tc.relPath, func(t *testing.T) { + result, err := ResolveURL(tc.serverURL, tc.relPath, tc.cleanQuery) + if tc.expectErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expected, result) + } + }) + } +} diff --git a/server/mdm/microsoft/microsoft_mdm.go b/server/mdm/microsoft/microsoft_mdm.go index 227f311ecc..a8a9254bd8 100644 --- a/server/mdm/microsoft/microsoft_mdm.go +++ b/server/mdm/microsoft/microsoft_mdm.go @@ -81,10 +81,6 @@ func ResolveWindowsMDMEnroll(serverURL string) (string, error) { return commonmdm.ResolveURL(serverURL, MDE2EnrollPath, false) } -func ResolveWindowsMDMAuth(serverURL string) (string, error) { - return commonmdm.ResolveURL(serverURL, MDE2AuthPath, false) -} - func ResolveWindowsMDMManagement(serverURL string) (string, error) { return commonmdm.ResolveURL(serverURL, MDE2ManagementPath, false) } diff --git a/server/mdm/microsoft/wstep_csr_test.go b/server/mdm/microsoft/wstep_csr_test.go new file mode 100644 index 0000000000..f5d5e59800 --- /dev/null +++ b/server/mdm/microsoft/wstep_csr_test.go @@ -0,0 +1,201 @@ +package microsoft_mdm + +import ( + "crypto/ecdsa" + "crypto/ed25519" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "encoding/base64" + "encoding/pem" + "net" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGetPublicKeyAlgorithmFromOID(t *testing.T) { + testCases := []struct { + oid asn1.ObjectIdentifier + expected x509.PublicKeyAlgorithm + }{ + {oidPublicKeyRSA, x509.RSA}, + {oidPublicKeyDSA, x509.DSA}, + {oidPublicKeyECDSA, x509.ECDSA}, + {oidPublicKeyEd25519, x509.Ed25519}, + {asn1.ObjectIdentifier{0, 0}, x509.UnknownPublicKeyAlgorithm}, + } + + for _, tc := range testCases { + t.Run(tc.oid.String(), func(t *testing.T) { + result := getPublicKeyAlgorithmFromOID(tc.oid) + require.Equal(t, tc.expected, result) + }) + } +} + +// The following tests were taken from the Go standard library (since the wstep +// code was taken from there as well) +// Copyright 2009 The Go Authors. All rights reserved. + +var pemPrivateKey = testingKey(` +-----BEGIN RSA TESTING KEY----- +MIICXAIBAAKBgQCxoeCUW5KJxNPxMp+KmCxKLc1Zv9Ny+4CFqcUXVUYH69L3mQ7v +IWrJ9GBfcaA7BPQqUlWxWM+OCEQZH1EZNIuqRMNQVuIGCbz5UQ8w6tS0gcgdeGX7 +J7jgCQ4RK3F/PuCM38QBLaHx988qG8NMc6VKErBjctCXFHQt14lerd5KpQIDAQAB +AoGAYrf6Hbk+mT5AI33k2Jt1kcweodBP7UkExkPxeuQzRVe0KVJw0EkcFhywKpr1 +V5eLMrILWcJnpyHE5slWwtFHBG6a5fLaNtsBBtcAIfqTQ0Vfj5c6SzVaJv0Z5rOd +7gQF6isy3t3w9IF3We9wXQKzT6q5ypPGdm6fciKQ8RnzREkCQQDZwppKATqQ41/R +vhSj90fFifrGE6aVKC1hgSpxGQa4oIdsYYHwMzyhBmWW9Xv/R+fPyr8ZwPxp2c12 +33QwOLPLAkEA0NNUb+z4ebVVHyvSwF5jhfJxigim+s49KuzJ1+A2RaSApGyBZiwS +rWvWkB471POAKUYt5ykIWVZ83zcceQiNTwJBAMJUFQZX5GDqWFc/zwGoKkeR49Yi +MTXIvf7Wmv6E++eFcnT461FlGAUHRV+bQQXGsItR/opIG7mGogIkVXa3E1MCQARX +AAA7eoZ9AEHflUeuLn9QJI/r0hyQQLEtrpwv6rDT1GCWaLII5HJ6NUFVf4TTcqxo +6vdM4QGKTJoO+SaCyP0CQFdpcxSAuzpFcKv0IlJ8XzS/cy+mweCMwyJ1PFEc4FX6 +wg/HcAJWY60xZTJDFN+Qfx8ZQvBEin6c2/h+zZi5IVY= +-----END RSA TESTING KEY----- +`) + +var testPrivateKey *rsa.PrivateKey + +func init() { + block, _ := pem.Decode([]byte(pemPrivateKey)) + + var err error + if testPrivateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes); err != nil { + panic("Failed to parse private key: " + err.Error()) + } +} + +func TestCreateCertificateRequest(t *testing.T) { + random := rand.Reader + + ecdsa256Priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatalf("Failed to generate ECDSA key: %s", err) + } + + ecdsa384Priv, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) + if err != nil { + t.Fatalf("Failed to generate ECDSA key: %s", err) + } + + ecdsa521Priv, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) + if err != nil { + t.Fatalf("Failed to generate ECDSA key: %s", err) + } + + _, ed25519Priv, err := ed25519.GenerateKey(random) + if err != nil { + t.Fatalf("Failed to generate Ed25519 key: %s", err) + } + + tests := []struct { + name string + priv interface{} + sigAlgo x509.SignatureAlgorithm + }{ + {"RSA", testPrivateKey, x509.SHA1WithRSA}, + {"ECDSA-256", ecdsa256Priv, x509.ECDSAWithSHA1}, + {"ECDSA-384", ecdsa384Priv, x509.ECDSAWithSHA1}, + {"ECDSA-521", ecdsa521Priv, x509.ECDSAWithSHA1}, + {"Ed25519", ed25519Priv, x509.PureEd25519}, + } + + for _, test := range tests { + template := x509.CertificateRequest{ + Subject: pkix.Name{ + CommonName: "test.example.com", + Organization: []string{"Ξ£ Acme Co"}, + }, + SignatureAlgorithm: test.sigAlgo, + DNSNames: []string{"test.example.com"}, + EmailAddresses: []string{"gopher@golang.org"}, + IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1).To4(), net.ParseIP("2001:4860:0:2001::68")}, + } + + derBytes, err := x509.CreateCertificateRequest(random, &template, test.priv) + if err != nil { + t.Errorf("%s: failed to create certificate request: %s", test.name, err) + continue + } + + out, err := ParseCertificateRequestFromWindowsDevice(derBytes) + if err != nil { + t.Errorf("%s: failed to create certificate request: %s", test.name, err) + continue + } + + err = out.CheckSignature() + if err != nil { + t.Errorf("%s: failed to check certificate request signature: %s", test.name, err) + continue + } + + if out.Subject.CommonName != template.Subject.CommonName { + t.Errorf("%s: output subject common name and template subject common name don't match", test.name) + } else if len(out.Subject.Organization) != len(template.Subject.Organization) { + t.Errorf("%s: output subject organisation and template subject organisation don't match", test.name) + } else if len(out.DNSNames) != len(template.DNSNames) { + t.Errorf("%s: output DNS names and template DNS names don't match", test.name) + } else if len(out.EmailAddresses) != len(template.EmailAddresses) { + t.Errorf("%s: output email addresses and template email addresses don't match", test.name) + } else if len(out.IPAddresses) != len(template.IPAddresses) { + t.Errorf("%s: output IP addresses and template IP addresses names don't match", test.name) + } + } +} + +func fromBase64(in string) []byte { + out := make([]byte, base64.StdEncoding.DecodedLen(len(in))) + n, err := base64.StdEncoding.Decode(out, []byte(in)) + if err != nil { + panic("failed to base64 decode") + } + return out[:n] +} + +func TestParseCertificateRequestFromWindowsDevice(t *testing.T) { + for _, csrBase64 := range csrBase64Array { + csrBytes := fromBase64(csrBase64) + csr, err := ParseCertificateRequestFromWindowsDevice(csrBytes) + if err != nil { + t.Fatalf("failed to parse CSR: %s", err) + } + + if len(csr.EmailAddresses) != 1 || csr.EmailAddresses[0] != "gopher@golang.org" { + t.Errorf("incorrect email addresses found: %v", csr.EmailAddresses) + } + + if len(csr.DNSNames) != 1 || csr.DNSNames[0] != "test.example.com" { + t.Errorf("incorrect DNS names found: %v", csr.DNSNames) + } + + if len(csr.Subject.Country) != 1 || csr.Subject.Country[0] != "AU" { + t.Errorf("incorrect Subject name: %v", csr.Subject) + } + } +} + +// These CSR was generated with OpenSSL: +// +// openssl req -out CSR.csr -new -sha256 -nodes -keyout privateKey.key -config openssl.cnf +// +// With openssl.cnf containing the following sections: +// +// [ v3_req ] +// basicConstraints = CA:FALSE +// keyUsage = nonRepudiation, digitalSignature, keyEncipherment +// subjectAltName = email:gopher@golang.org,DNS:test.example.com +// [ req_attributes ] +// challengePassword = ignored challenge +// unstructuredName = ignored unstructured name +var csrBase64Array = [...]string{ + // Just [ v3_req ] + "MIIDHDCCAgQCAQAwfjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLQ29tbW9uIE5hbWUxITAfBgkqhkiG9w0BCQEWEnRlc3RAZW1haWwuYWRkcmVzczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK1GY4YFx2ujlZEOJxQVYmsjUnLsd5nFVnNpLE4cV+77sgv9NPNlB8uhn3MXt5leD34rm/2BisCHOifPucYlSrszo2beuKhvwn4+2FxDmWtBEMu/QA16L5IvoOfYZm/gJTsPwKDqvaR0tTU67a9OtxwNTBMI56YKtmwd/o8d3hYv9cg+9ZGAZ/gKONcg/OWYx/XRh6bd0g8DMbCikpWgXKDsvvK1Nk+VtkDO1JxuBaj4Lz/p/MifTfnHoqHxWOWl4EaTs4Ychxsv34/rSj1KD1tJqorIv5Xv2aqv4sjxfbrYzX4kvS5SC1goIovLnhj5UjmQ3Qy8u65eow/LLWw+YFcCAwEAAaBZMFcGCSqGSIb3DQEJDjFKMEgwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwLgYDVR0RBCcwJYERZ29waGVyQGdvbGFuZy5vcmeCEHRlc3QuZXhhbXBsZS5jb20wDQYJKoZIhvcNAQELBQADggEBAB6VPMRrchvNW61Tokyq3ZvO6/NoGIbuwUn54q6l5VZW0Ep5Nq8juhegSSnaJ0jrovmUgKDN9vEo2KxuAtwG6udS6Ami3zP+hRd4k9Q8djJPb78nrjzWiindLK5Fps9U5mMoi1ER8ViveyAOTfnZt/jsKUaRsscY2FzE9t9/o5moE6LTcHUS4Ap1eheR+J72WOnQYn3cifYaemsA9MJuLko+kQ6xseqttbh9zjqd9fiCSh/LNkzos9c+mg2yMADitaZinAh+HZi50ooEbjaT3erNq9O6RqwJlgD00g6MQdoz9bTAryCUhCQfkIaepmQ7BxS0pqWNW3MMwfDwx/Snz6g=", + // Both [ v3_req ] and [ req_attributes ] + "MIIDaTCCAlECAQAwfjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLQ29tbW9uIE5hbWUxITAfBgkqhkiG9w0BCQEWEnRlc3RAZW1haWwuYWRkcmVzczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK1GY4YFx2ujlZEOJxQVYmsjUnLsd5nFVnNpLE4cV+77sgv9NPNlB8uhn3MXt5leD34rm/2BisCHOifPucYlSrszo2beuKhvwn4+2FxDmWtBEMu/QA16L5IvoOfYZm/gJTsPwKDqvaR0tTU67a9OtxwNTBMI56YKtmwd/o8d3hYv9cg+9ZGAZ/gKONcg/OWYx/XRh6bd0g8DMbCikpWgXKDsvvK1Nk+VtkDO1JxuBaj4Lz/p/MifTfnHoqHxWOWl4EaTs4Ychxsv34/rSj1KD1tJqorIv5Xv2aqv4sjxfbrYzX4kvS5SC1goIovLnhj5UjmQ3Qy8u65eow/LLWw+YFcCAwEAAaCBpTAgBgkqhkiG9w0BCQcxEwwRaWdub3JlZCBjaGFsbGVuZ2UwKAYJKoZIhvcNAQkCMRsMGWlnbm9yZWQgdW5zdHJ1Y3R1cmVkIG5hbWUwVwYJKoZIhvcNAQkOMUowSDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DAuBgNVHREEJzAlgRFnb3BoZXJAZ29sYW5nLm9yZ4IQdGVzdC5leGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAgxe2N5O48EMsYE7o0rZBB0wi3Ov5/yYfnmmVI22Y3sP6VXbLDW0+UWIeSccOhzUCcZ/G4qcrfhhx6gTZTeA01nP7TdTJURvWAH5iFqj9sQ0qnLq6nEcVHij3sG6M5+BxAIVClQBk6lTCzgphc835Fjj6qSLuJ20XHdL5UfUbiJxx299CHgyBRL+hBUIPfz8p+ZgamyAuDLfnj54zzcRVyLlrmMLNPZNll1Q70RxoU6uWvLH8wB8vQe3Q/guSGubLyLRTUQVPh+dw1L4t8MKFWfX/48jwRM4gIRHFHPeAAE9D9YAoqdIvj/iFm/eQ++7DP8MDwOZWsXeB6jjwHuLmkQ==", +} diff --git a/server/vulnerabilities/utils/rpmvercmp.go b/server/vulnerabilities/utils/rpmvercmp.go index 7c5016f1ca..beabd1035d 100644 --- a/server/vulnerabilities/utils/rpmvercmp.go +++ b/server/vulnerabilities/utils/rpmvercmp.go @@ -69,9 +69,9 @@ func (s segment) compare(b segment) int { return 0 } else if *s.number < *b.number { return -1 - } else { - return 1 } + + return 1 // 'a' is a number seg, 'b' is a letter seg, // numbers are always greater than letters } else if s.number != nil && b.number == nil { @@ -81,12 +81,12 @@ func (s segment) compare(b segment) int { return -1 // Both segs are letters, then we just // compare them - } else { - if s.letters == b.letters { - return 0 - } - return strings.Compare(s.letters, b.letters) } + + if s.letters == b.letters { + return 0 + } + return strings.Compare(s.letters, b.letters) } // Returns the next maximal alphabetic or numeric segment,