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 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 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/.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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 521746d6c3..8d18839ae8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,47 @@ +## 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. +- 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. +- 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 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 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: 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. + + 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 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 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 diff --git a/infrastructure/dogfood/terraform/aws/variables.tf b/infrastructure/dogfood/terraform/aws/variables.tf index 991c9135de..cb0d1593de 100644 --- a/infrastructure/dogfood/terraform/aws/variables.tf +++ b/infrastructure/dogfood/terraform/aws/variables.tf @@ -56,7 +56,7 @@ variable "database_name" { variable "fleet_image" { description = "the name of the container image to run" - default = "fleetdm/fleet:v4.47.3" + default = "fleetdm/fleet:v4.48.0" } variable "software_inventory" { diff --git a/infrastructure/dogfood/terraform/gcp/variables.tf b/infrastructure/dogfood/terraform/gcp/variables.tf index b3cddde5ca..afe4e46243 100644 --- a/infrastructure/dogfood/terraform/gcp/variables.tf +++ b/infrastructure/dogfood/terraform/gcp/variables.tf @@ -68,5 +68,5 @@ variable "redis_mem" { } variable "image" { - default = "fleet:v4.47.3" + default = "fleet:v4.48.0" } 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= 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 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: 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 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 | - | - | - | 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/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}, 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/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/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/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/schema/osquery_fleet_schema.json b/schema/osquery_fleet_schema.json index c4b7de39e2..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", @@ -18786,6 +18780,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.", @@ -19795,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", @@ -19850,8 +20024,7 @@ "notes": "", "hidden": false, "required": false, - "index": false, - "requires_user_context": true + "index": false }, { "name": "host", @@ -22842,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", @@ -22852,8 +23025,7 @@ "notes": "", "hidden": false, "required": false, - "index": true, - "requires_user_context": true + "index": true }, { "name": "name", @@ -24342,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", @@ -24352,8 +24524,7 @@ "notes": "", "hidden": false, "required": false, - "index": false, - "requires_user_context": true + "index": false }, { "name": "time", @@ -25024,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", @@ -25034,8 +25205,7 @@ "notes": "", "hidden": false, "required": false, - "index": false, - "requires_user_context": true + "index": false }, { "name": "block", @@ -26918,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", @@ -26928,8 +27098,7 @@ "notes": "", "hidden": false, "required": false, - "index": false, - "requires_user_context": true + "index": false }, { "name": "path", @@ -27509,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/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/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 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 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 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..adfd76509e 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( @@ -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 @@ -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) } @@ -676,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 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/calendar_events.go b/server/fleet/calendar_events.go index 3152ee65bb..d7b22d478e 100644 --- a/server/fleet/calendar_events.go +++ b/server/fleet/calendar_events.go @@ -18,6 +18,8 @@ const ( CalendarWebhookStatusNone CalendarWebhookStatus = iota CalendarWebhookStatusPending CalendarWebhookStatusSent + CalendarWebhookStatusError + CalendarWebhookStatusRetry ) type HostCalendarEvent struct { 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/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/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") } 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/osquery.go b/server/service/osquery.go index 379afafd6a..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" @@ -1133,16 +1135,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 } @@ -1160,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/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"] 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, 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/terraform/byo-vpc/README.md b/terraform/byo-vpc/README.md index 27920c1819..0cc44fdec0 100644 --- a/terraform/byo-vpc/README.md +++ b/terraform/byo-vpc/README.md @@ -33,7 +33,7 @@ No requirements. |------|-------------|------|---------|:--------:| | [alb\_config](#input\_alb\_config) | n/a |
object({
name = optional(string, "fleet")
subnets = list(string)
security_groups = optional(list(string), [])
access_logs = optional(map(string), {})
certificate_arn = string
allowed_cidrs = optional(list(string), ["0.0.0.0/0"])
allowed_ipv6_cidrs = optional(list(string), ["::/0"])
egress_cidrs = optional(list(string), ["0.0.0.0/0"])
egress_ipv6_cidrs = optional(list(string), ["::/0"])
extra_target_groups = optional(any, [])
https_listener_rules = optional(any, [])
tls_policy = optional(string, "ELBSecurityPolicy-TLS-1-2-2017-01")
idle_timeout = optional(number, 60)
})
| n/a | yes | | [ecs\_cluster](#input\_ecs\_cluster) | The config for the terraform-aws-modules/ecs/aws module |
object({
autoscaling_capacity_providers = optional(any, {})
cluster_configuration = optional(any, {
execute_command_configuration = {
logging = "OVERRIDE"
log_configuration = {
cloud_watch_log_group_name = "/aws/ecs/aws-ec2"
}
}
})
cluster_name = optional(string, "fleet")
cluster_settings = optional(map(string), {
"name" : "containerInsights",
"value" : "enabled",
})
create = optional(bool, true)
default_capacity_provider_use_fargate = optional(bool, true)
fargate_capacity_providers = optional(any, {
FARGATE = {
default_capacity_provider_strategy = {
weight = 100
}
}
FARGATE_SPOT = {
default_capacity_provider_strategy = {
weight = 0
}
}
})
tags = optional(map(string))
})
|
{
"autoscaling_capacity_providers": {},
"cluster_configuration": {
"execute_command_configuration": {
"log_configuration": {
"cloud_watch_log_group_name": "/aws/ecs/aws-ec2"
},
"logging": "OVERRIDE"
}
},
"cluster_name": "fleet",
"cluster_settings": {
"name": "containerInsights",
"value": "enabled"
},
"create": true,
"default_capacity_provider_use_fargate": true,
"fargate_capacity_providers": {
"FARGATE": {
"default_capacity_provider_strategy": {
"weight": 100
}
},
"FARGATE_SPOT": {
"default_capacity_provider_strategy": {
"weight": 0
}
}
},
"tags": {}
}
| no | -| [fleet\_config](#input\_fleet\_config) | The configuration object for Fleet itself. Fields that default to null will have their respective resources created if not specified. |
object({
mem = optional(number, 4096)
cpu = optional(number, 512)
image = optional(string, "fleetdm/fleet:v4.47.3")
family = optional(string, "fleet")
sidecars = optional(list(any), [])
depends_on = optional(list(any), [])
mount_points = optional(list(any), [])
volumes = optional(list(any), [])
extra_environment_variables = optional(map(string), {})
extra_iam_policies = optional(list(string), [])
extra_execution_iam_policies = optional(list(string), [])
extra_secrets = optional(map(string), {})
security_groups = optional(list(string), null)
security_group_name = optional(string, "fleet")
iam_role_arn = optional(string, null)
service = optional(object({
name = optional(string, "fleet")
}), {
name = "fleet"
})
database = optional(object({
password_secret_arn = string
user = string
database = string
address = string
rr_address = optional(string, null)
}), {
password_secret_arn = null
user = null
database = null
address = null
rr_address = null
})
redis = optional(object({
address = string
use_tls = optional(bool, true)
}), {
address = null
use_tls = true
})
awslogs = optional(object({
name = optional(string, null)
region = optional(string, null)
create = optional(bool, true)
prefix = optional(string, "fleet")
retention = optional(number, 5)
}), {
name = null
region = null
prefix = "fleet"
retention = 5
})
loadbalancer = optional(object({
arn = string
}), {
arn = null
})
extra_load_balancers = optional(list(any), [])
networking = optional(object({
subnets = list(string)
security_groups = optional(list(string), null)
}), {
subnets = null
security_groups = null
})
autoscaling = optional(object({
max_capacity = optional(number, 5)
min_capacity = optional(number, 1)
memory_tracking_target_value = optional(number, 80)
cpu_tracking_target_value = optional(number, 80)
}), {
max_capacity = 5
min_capacity = 1
memory_tracking_target_value = 80
cpu_tracking_target_value = 80
})
iam = optional(object({
role = optional(object({
name = optional(string, "fleet-role")
policy_name = optional(string, "fleet-iam-policy")
}), {
name = "fleet-role"
policy_name = "fleet-iam-policy"
})
execution = optional(object({
name = optional(string, "fleet-execution-role")
policy_name = optional(string, "fleet-execution-role")
}), {
name = "fleet-execution-role"
policy_name = "fleet-iam-policy-execution"
})
}), {
name = "fleetdm-execution-role"
})
})
|
{
"autoscaling": {
"cpu_tracking_target_value": 80,
"max_capacity": 5,
"memory_tracking_target_value": 80,
"min_capacity": 1
},
"awslogs": {
"create": true,
"name": null,
"prefix": "fleet",
"region": null,
"retention": 5
},
"cpu": 256,
"database": {
"address": null,
"database": null,
"password_secret_arn": null,
"rr_address": null,
"user": null
},
"depends_on": [],
"extra_environment_variables": {},
"extra_execution_iam_policies": [],
"extra_iam_policies": [],
"extra_load_balancers": [],
"extra_secrets": {},
"family": "fleet",
"iam": {
"execution": {
"name": "fleet-execution-role",
"policy_name": "fleet-iam-policy-execution"
},
"role": {
"name": "fleet-role",
"policy_name": "fleet-iam-policy"
}
},
"iam_role_arn": null,
"image": "fleetdm/fleet:v4.31.1",
"loadbalancer": {
"arn": null
},
"mem": 512,
"mount_points": [],
"networking": {
"security_groups": null,
"subnets": null
},
"redis": {
"address": null,
"use_tls": true
},
"security_group_name": "fleet",
"security_groups": null,
"service": {
"name": "fleet"
},
"sidecars": [],
"volumes": []
}
| no | +| [fleet\_config](#input\_fleet\_config) | The configuration object for Fleet itself. Fields that default to null will have their respective resources created if not specified. |
object({
mem = optional(number, 4096)
cpu = optional(number, 512)
image = optional(string, "fleetdm/fleet:v4.48.0")
family = optional(string, "fleet")
sidecars = optional(list(any), [])
depends_on = optional(list(any), [])
mount_points = optional(list(any), [])
volumes = optional(list(any), [])
extra_environment_variables = optional(map(string), {})
extra_iam_policies = optional(list(string), [])
extra_execution_iam_policies = optional(list(string), [])
extra_secrets = optional(map(string), {})
security_groups = optional(list(string), null)
security_group_name = optional(string, "fleet")
iam_role_arn = optional(string, null)
service = optional(object({
name = optional(string, "fleet")
}), {
name = "fleet"
})
database = optional(object({
password_secret_arn = string
user = string
database = string
address = string
rr_address = optional(string, null)
}), {
password_secret_arn = null
user = null
database = null
address = null
rr_address = null
})
redis = optional(object({
address = string
use_tls = optional(bool, true)
}), {
address = null
use_tls = true
})
awslogs = optional(object({
name = optional(string, null)
region = optional(string, null)
create = optional(bool, true)
prefix = optional(string, "fleet")
retention = optional(number, 5)
}), {
name = null
region = null
prefix = "fleet"
retention = 5
})
loadbalancer = optional(object({
arn = string
}), {
arn = null
})
extra_load_balancers = optional(list(any), [])
networking = optional(object({
subnets = list(string)
security_groups = optional(list(string), null)
}), {
subnets = null
security_groups = null
})
autoscaling = optional(object({
max_capacity = optional(number, 5)
min_capacity = optional(number, 1)
memory_tracking_target_value = optional(number, 80)
cpu_tracking_target_value = optional(number, 80)
}), {
max_capacity = 5
min_capacity = 1
memory_tracking_target_value = 80
cpu_tracking_target_value = 80
})
iam = optional(object({
role = optional(object({
name = optional(string, "fleet-role")
policy_name = optional(string, "fleet-iam-policy")
}), {
name = "fleet-role"
policy_name = "fleet-iam-policy"
})
execution = optional(object({
name = optional(string, "fleet-execution-role")
policy_name = optional(string, "fleet-execution-role")
}), {
name = "fleet-execution-role"
policy_name = "fleet-iam-policy-execution"
})
}), {
name = "fleetdm-execution-role"
})
})
|
{
"autoscaling": {
"cpu_tracking_target_value": 80,
"max_capacity": 5,
"memory_tracking_target_value": 80,
"min_capacity": 1
},
"awslogs": {
"create": true,
"name": null,
"prefix": "fleet",
"region": null,
"retention": 5
},
"cpu": 256,
"database": {
"address": null,
"database": null,
"password_secret_arn": null,
"rr_address": null,
"user": null
},
"depends_on": [],
"extra_environment_variables": {},
"extra_execution_iam_policies": [],
"extra_iam_policies": [],
"extra_load_balancers": [],
"extra_secrets": {},
"family": "fleet",
"iam": {
"execution": {
"name": "fleet-execution-role",
"policy_name": "fleet-iam-policy-execution"
},
"role": {
"name": "fleet-role",
"policy_name": "fleet-iam-policy"
}
},
"iam_role_arn": null,
"image": "fleetdm/fleet:v4.31.1",
"loadbalancer": {
"arn": null
},
"mem": 512,
"mount_points": [],
"networking": {
"security_groups": null,
"subnets": null
},
"redis": {
"address": null,
"use_tls": true
},
"security_group_name": "fleet",
"security_groups": null,
"service": {
"name": "fleet"
},
"sidecars": [],
"volumes": []
}
| no | | [migration\_config](#input\_migration\_config) | The configuration object for Fleet's migration task. |
object({
mem = number
cpu = number
})
|
{
"cpu": 1024,
"mem": 2048
}
| no | | [rds\_config](#input\_rds\_config) | The config for the terraform-aws-modules/rds-aurora/aws module |
object({
name = optional(string, "fleet")
engine_version = optional(string, "8.0.mysql_aurora.3.02.2")
instance_class = optional(string, "db.t4g.large")
subnets = optional(list(string), [])
allowed_security_groups = optional(list(string), [])
allowed_cidr_blocks = optional(list(string), [])
apply_immediately = optional(bool, true)
monitoring_interval = optional(number, 10)
db_parameter_group_name = optional(string)
db_parameters = optional(map(string), {})
db_cluster_parameter_group_name = optional(string)
db_cluster_parameters = optional(map(string), {})
enabled_cloudwatch_logs_exports = optional(list(string), [])
master_username = optional(string, "fleet")
snapshot_identifier = optional(string)
cluster_tags = optional(map(string), {})
preferred_maintenance_window = optional(string, "thu:23:00-fri:00:00")
})
|
{
"allowed_cidr_blocks": [],
"allowed_security_groups": [],
"apply_immediately": true,
"cluster_tags": {},
"db_cluster_parameter_group_name": null,
"db_cluster_parameters": {},
"db_parameter_group_name": null,
"db_parameters": {},
"enabled_cloudwatch_logs_exports": [],
"engine_version": "8.0.mysql_aurora.3.02.2",
"instance_class": "db.t4g.large",
"master_username": "fleet",
"monitoring_interval": 10,
"name": "fleet",
"preferred_maintenance_window": "thu:23:00-fri:00:00",
"snapshot_identifier": null,
"subnets": []
}
| no | | [redis\_config](#input\_redis\_config) | n/a |
object({
name = optional(string, "fleet")
replication_group_id = optional(string)
elasticache_subnet_group_name = optional(string, "")
allowed_security_group_ids = optional(list(string), [])
subnets = list(string)
allowed_cidrs = list(string)
availability_zones = optional(list(string), [])
cluster_size = optional(number, 3)
instance_type = optional(string, "cache.m5.large")
apply_immediately = optional(bool, true)
automatic_failover_enabled = optional(bool, false)
engine_version = optional(string, "6.x")
family = optional(string, "redis6.x")
at_rest_encryption_enabled = optional(bool, true)
transit_encryption_enabled = optional(bool, true)
parameter = optional(list(object({
name = string
value = string
})), [])
log_delivery_configuration = optional(list(map(any)), [])
tags = optional(map(string), {})
})
|
{
"allowed_cidrs": null,
"allowed_security_group_ids": [],
"apply_immediately": true,
"at_rest_encryption_enabled": true,
"automatic_failover_enabled": false,
"availability_zones": [],
"cluster_size": 3,
"elasticache_subnet_group_name": "",
"engine_version": "6.x",
"family": "redis6.x",
"instance_type": "cache.m5.large",
"log_delivery_configuration": [],
"name": "fleet",
"parameter": [],
"replication_group_id": null,
"subnets": null,
"tags": {},
"transit_encryption_enabled": true
}
| no | diff --git a/terraform/byo-vpc/byo-db/byo-ecs/variables.tf b/terraform/byo-vpc/byo-db/byo-ecs/variables.tf index 367832f457..20b8249bff 100644 --- a/terraform/byo-vpc/byo-db/byo-ecs/variables.tf +++ b/terraform/byo-vpc/byo-db/byo-ecs/variables.tf @@ -13,7 +13,7 @@ variable "fleet_config" { type = object({ mem = optional(number, 4096) cpu = optional(number, 512) - image = optional(string, "fleetdm/fleet:v4.47.3") + image = optional(string, "fleetdm/fleet:v4.48.0") family = optional(string, "fleet") sidecars = optional(list(any), []) depends_on = optional(list(any), []) diff --git a/terraform/byo-vpc/byo-db/variables.tf b/terraform/byo-vpc/byo-db/variables.tf index 7c8ff1738f..88cb0c03fb 100644 --- a/terraform/byo-vpc/byo-db/variables.tf +++ b/terraform/byo-vpc/byo-db/variables.tf @@ -74,7 +74,7 @@ variable "fleet_config" { type = object({ mem = optional(number, 4096) cpu = optional(number, 512) - image = optional(string, "fleetdm/fleet:v4.47.3") + image = optional(string, "fleetdm/fleet:v4.48.0") family = optional(string, "fleet") sidecars = optional(list(any), []) depends_on = optional(list(any), []) diff --git a/terraform/byo-vpc/example/main.tf b/terraform/byo-vpc/example/main.tf index daefdcbc54..28311ecee1 100644 --- a/terraform/byo-vpc/example/main.tf +++ b/terraform/byo-vpc/example/main.tf @@ -17,7 +17,7 @@ provider "aws" { } locals { - fleet_image = "fleetdm/fleet:v4.47.3" + fleet_image = "fleetdm/fleet:v4.48.0" domain_name = "example.com" } diff --git a/terraform/byo-vpc/variables.tf b/terraform/byo-vpc/variables.tf index f74561183f..4ad249b0b0 100644 --- a/terraform/byo-vpc/variables.tf +++ b/terraform/byo-vpc/variables.tf @@ -167,7 +167,7 @@ variable "fleet_config" { type = object({ mem = optional(number, 4096) cpu = optional(number, 512) - image = optional(string, "fleetdm/fleet:v4.47.3") + image = optional(string, "fleetdm/fleet:v4.48.0") family = optional(string, "fleet") sidecars = optional(list(any), []) depends_on = optional(list(any), []) diff --git a/terraform/example/main.tf b/terraform/example/main.tf index 5a2fcf5491..04b2691900 100644 --- a/terraform/example/main.tf +++ b/terraform/example/main.tf @@ -59,8 +59,8 @@ module "fleet" { fleet_config = { # To avoid pull-rate limiting from dockerhub, consider using our quay.io mirror - # for the Fleet image. e.g. "quay.io/fleetdm/fleet:v4.47.3" - image = "fleetdm/fleet:v4.47.3" # override default to deploy the image you desire + # for the Fleet image. e.g. "quay.io/fleetdm/fleet:v4.48.0" + image = "fleetdm/fleet:v4.48.0" # override default to deploy the image you desire # See https://fleetdm.com/docs/deploy/reference-architectures#aws for appropriate scaling # memory and cpu. autoscaling = { diff --git a/terraform/variables.tf b/terraform/variables.tf index e8b6e29b67..a5cb956185 100644 --- a/terraform/variables.tf +++ b/terraform/variables.tf @@ -215,7 +215,7 @@ variable "fleet_config" { type = object({ mem = optional(number, 4096) cpu = optional(number, 512) - image = optional(string, "fleetdm/fleet:v4.47.3") + image = optional(string, "fleetdm/fleet:v4.48.0") family = optional(string, "fleet") sidecars = optional(list(any), []) depends_on = optional(list(any), []) 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." 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", + )))) +} diff --git a/tools/fleetctl-npm/package.json b/tools/fleetctl-npm/package.json index 19de34bf6c..80a5a278cb 100644 --- a/tools/fleetctl-npm/package.json +++ b/tools/fleetctl-npm/package.json @@ -1,6 +1,6 @@ { "name": "fleetctl", - "version": "v4.47.3", + "version": "v4.48.0", "description": "Installer for the fleetctl CLI tool", "bin": { "fleetctl": "./run.js" 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, 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/patch_release.sh b/tools/release/publish_release.sh similarity index 63% rename from tools/release/patch_release.sh rename to tools/release/publish_release.sh index 0acd0ce7bf..6cc615af17 100755 --- a/tools/release/patch_release.sh +++ b/tools/release/publish_release.sh @@ -45,14 +45,14 @@ # :;::, # # -# /$$$$$$$$ /$$ /$$$$$$$$ /$$$$$$$$ /$$$$$$$$ /$$$$$$$ /$$$$$$ /$$$$$$$$ /$$$$$$ /$$ /$$ -# | $$_____/| $$ | $$_____/| $$_____/|__ $$__/ | $$__ $$ /$$__ $$|__ $$__//$$__ $$| $$ | $$ -# | $$ | $$ | $$ | $$ | $$ | $$ \ $$| $$ \ $$ | $$ | $$ \__/| $$ | $$ -# | $$$$$ | $$ | $$$$$ | $$$$$ | $$ | $$$$$$$/| $$$$$$$$ | $$ | $$ | $$$$$$$$ -# | $$__/ | $$ | $$__/ | $$__/ | $$ | $$____/ | $$__ $$ | $$ | $$ | $$__ $$ -# | $$ | $$ | $$ | $$ | $$ | $$ | $$ | $$ | $$ | $$ $$| $$ | $$ -# | $$ | $$$$$$$$| $$$$$$$$| $$$$$$$$ | $$ | $$ | $$ | $$ | $$ | $$$$$$/| $$ | $$ -# |__/ |________/|________/|________/ |__/ |__/ |__/ |__/ |__/ \______/ |__/ |__/ +# /$$$$$$$$ /$$ /$$$$$$$$ /$$$$$$$$ /$$$$$$$$ +# | $$_____/| $$ | $$_____/| $$_____/|__ $$__/ +# | $$ | $$ | $$ | $$ | $$ +# | $$$$$ | $$ | $$$$$ | $$$$$ | $$ +# | $$__/ | $$ | $$__/ | $$__/ | $$ +# | $$ | $$ | $$ | $$ | $$ +# | $$ | $$$$$$$$| $$$$$$$$| $$$$$$$$ | $$ +# |__/ |________/|________/|________/ |__/ # # /$$$$$$$ /$$$$$$$$ /$$ /$$$$$$$$ /$$$$$$ /$$$$$$ /$$$$$$$$ /$$$$$$$ # | $$__ $$| $$_____/| $$ | $$_____/ /$$__ $$ /$$__ $$| $$_____/| $$__ $$ @@ -64,14 +64,18 @@ # |__/ |__/|________/|________/|________/|__/ |__/ \______/ |________/|__/ |__/ # +failed=false + usage() { echo "Usage: $0 [options] (optional|start_version)" echo "" echo "Options:" + echo " -a, --main_release This is a release based off of main and not a tagged patch." echo " -c, --cherry_pick_resolved The script has been run, had merge conflicts, and those have been resolved and all cherry picks completed manually." 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" @@ -82,7 +86,10 @@ 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" echo "" echo "Examples:" echo " $0 -d Dry run the script" @@ -175,85 +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() { - echo - echo "For announcing in #help-engineering" - echo "====================================================" - echo "Release $target_milestone QA ticket and docker publish" - echo "QA ticket for Release $target_milestone " `gh issue list --search "Release QA: $target_milestone in:title" --json url | jq -r .[0].url` - echo "Docker Deploy status " `gh run list --workflow goreleaser-snapshot-fleet.yaml --json event,url,headBranch --limit 100 | jq -r "[.[]|select(.headBranch==\"$target_patch_branch\")][0].url"` - echo "List of tickets pulled into release https://github.com/fleetdm/fleet/milestone/$target_milestone_number" - echo + 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 - general_channel_id=B06RZ60NUHX/tzaDZOvFCSvS2HC6rECi3Mvu - help_infra_channel_id=B06RLDFLC75/biuacbLxWRsDhv0hLA2qnLbX - help_eng_channel_id=B06RDTMUP1U/x2R36PXvW13KE6daxMiUK6W7 - 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/$general_channel_id + 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/$help_infra_channel_id + 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 @@ -271,11 +459,14 @@ target_version="" print_info=false publish_release=false release_notes=false +do_tag=false +main_release=false # Parse long options manually for arg in "$@"; do shift case "$arg" in + "--main_release") set -- "$@" "-a" ;; "--cherry_pick_resolved") set -- "$@" "-c" ;; "--dry-run") set -- "$@" "-d" ;; "--force") set -- "$@" "-f" ;; @@ -286,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" @@ -293,12 +485,14 @@ for arg in "$@"; do done # Extract options and their arguments using getopts -while getopts "cdfhmo: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 ;; @@ -345,6 +539,19 @@ if [ -z "$open_api_key" ]; then fi fi +if [ -z "$SLACK_GENERAL_TOKEN" ]; then + echo "Error: No SLACK_GENERAL_TOKEN environment variable." >&2 + exit 1 +fi +if [ -z "$SLACK_HELP_INFRA_TOKEN" ]; then + echo "Error: No SLACK_HELP_INFRA_TOKEN environment variable." >&2 + exit 1 +fi +if [ -z "$SLACK_HELP_ENG_TOKEN" ]; then + echo "Error: No SLACK_HELP_ENG_TOKEN environment variable." >&2 + exit 1 +fi + if [[ "$target_date" != "" ]]; then validate_and_format_date $target_date fi @@ -365,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 @@ -384,7 +596,13 @@ fi start_ver_tag=fleet-$start_version -echo "Patch release from $start_version to $next_ver" +if [[ "$main_release" == "true" ]]; then + echo "Main release from $start_version to $next_ver" + start_ver_tag=main +else + echo "Patch release from $start_version to $next_ver" +fi + if [ "$force" = "false" ]; then read -r -p "If this is correct confirm yes to continue? [y/N] " response case "$response" in @@ -398,51 +616,57 @@ if [ "$force" = "false" ]; then fi # 4.47.2 start_milestone="${start_version:1}" -# 4.47.3 +# 4.48.0 target_milestone="${next_ver:1}" # 79 target_milestone_number=`gh api repos/:owner/:repo/milestones | jq -r ".[] | select(.title==\"$target_milestone\") | .number"` -# patch-fleet-v4.47.3 +# patch-fleet-v4.48.0 target_patch_branch="patch-fleet-$next_ver" -# fleet-v4.47.3 +if [[ "$main_release" == "true" ]]; then + target_patch_branch="prepare-fleet-$next_ver" +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 @@ -455,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'` @@ -483,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 ;; *) @@ -494,249 +716,113 @@ if [ "$cherry_pick_resolved" = "false" ]; then commits="" - for pr in ${total_prs[*]}; - do - output=`gh pr view $pr --json state,mergeCommit,baseRefName` - state=`echo $output | jq -r .state` - commit=`echo $output | jq -r .mergeCommit.oid` - target_branch=`echo $output | jq -r .baseRefName` - echo -n "$pr $state $commit $target_branch:" - if [[ "$state" != "MERGED" || "$target_branch" != "main" ]]; then - echo " WARNING - Skipping pr https://github.com/fleetdm/fleet/pull/$pr" - else - if [[ "$commit" != "" && "$commit" != "null" ]]; then - echo " Commit looks valid - $commit, adding to cherry-pick" - commits+="$commit " + 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` + state=`echo $output | jq -r .state` + commit=`echo $output | jq -r .mergeCommit.oid` + target_branch=`echo $output | jq -r .baseRefName` + echo -n "$pr $state $commit $target_branch:" + if [[ "$state" != "MERGED" || "$target_branch" != "main" ]]; then + echo " WARNING - Skipping pr https://github.com/fleetdm/fleet/pull/$pr" else - echo " WARNING - invalid commit for pr https://github.com/fleetdm/fleet/pull/$pr - $commit" + if [[ "$commit" != "" && "$commit" != "null" ]]; then + echo " Commit looks valid - $commit, adding to cherry-pick" + commits+="$commit " + else + echo " WARNING - invalid commit for pr https://github.com/fleetdm/fleet/pull/$pr - $commit" + fi fi - fi - #echo "=======================================" - done + #echo "=======================================" + done - for commit in $commits; - do - # echo $commit - timestamp=`git log -n 1 --pretty=format:%at $commit` - if [ $? -ne 0 ]; then - echo "Failed to identify $commit, exiting" - exit 1 - fi - # echo $timestamp - time_map[$timestamp]=$commit - done + for commit in $commits; + do + # echo $commit + timestamp=`git log -n 1 --pretty=format:%at $commit` + if [ $? -ne 0 ]; then + echo "Failed to identify $commit, exiting" + exit 1 + fi + # echo $timestamp + time_map[$timestamp]=$commit + done - timestamps="" - for key in "${!time_map[@]}"; do - timestamps+="$key\n" - done - for ts in `echo -e $timestamps | sort`; do - commit_hash="${time_map[$ts]}" - # echo "# $ts $commit_hash" - if git branch --contains "$commit_hash" | $GREP_CMD -q "$(git rev-parse --abbrev-ref HEAD)"; then - echo "# Commit $commit_hash is on the current branch." - is_on_current_branch=true - else - # echo "# Commit $commit_hash is not on the current branch." - if [[ "$failed" == "false" ]]; then + timestamps="" + for key in "${!time_map[@]}"; do + timestamps+="$key\n" + done + for ts in `echo -e $timestamps | sort`; do + commit_hash="${time_map[$ts]}" + # echo "# $ts $commit_hash" + if git branch --contains "$commit_hash" | $GREP_CMD -q "$(git rev-parse --abbrev-ref HEAD)"; then + echo "# Commit $commit_hash is on the current branch." + is_on_current_branch=true + else + # echo "# Commit $commit_hash is not on the current branch." + if [[ "$failed" == "false" ]]; then - if [ "$dry_run" = "false" ]; then - git cherry-pick $commit_hash - if [ $? -ne 0 ]; then - echo "Cherry pick of $commit_hash failed. Please resolve then continue the cherry-picks manually" - failed=true + if [ "$dry_run" = "false" ]; then + git cherry-pick $commit_hash + if [ $? -ne 0 ]; then + echo "Cherry pick of $commit_hash failed. Please resolve then continue the cherry-picks manually" + failed=true + fi + else + echo "DRYRUN: Would have cherry picked $commit_hash" fi else - echo "DRYRUN: Would have cherry picked $commit_hash" + echo "git cherry-pick $commit_hash" fi - else - echo "git cherry-pick $commit_hash" + is_on_current_branch=false fi - is_on_current_branch=false - fi - done + done + fi 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..." diff --git a/website/api/controllers/entrance/signup.js b/website/api/controllers/entrance/signup.js index 652b20e802..5c970562e6 100644 --- a/website/api/controllers/entrance/signup.js +++ b/website/api/controllers/entrance/signup.js @@ -63,18 +63,6 @@ the account verification message.)`, defaultsTo: 'Buy a license', }, - primaryBuyingSituation: { - type: 'string', - description: 'What the user will be using Fleet for.', - required: true, - isIn: [ - 'eo-security', - 'eo-it', - 'mdm', - 'vm' - ], - } - }, @@ -104,7 +92,7 @@ the account verification message.)`, }, - fn: async function ({emailAddress, password, firstName, lastName, organization, signupReason, primaryBuyingSituation}) { + fn: async function ({emailAddress, password, firstName, lastName, organization, signupReason}) { // Note: in Oct. 2023, the Fleet Sandbox related code was removed from this action. For more details, see https://github.com/fleetdm/fleet/pull/14638/files var newEmailAddress = emailAddress.toLowerCase(); @@ -153,7 +141,6 @@ the account verification message.)`, signupReason, password: await sails.helpers.passwords.hashPassword(password), stripeCustomerId, - primaryBuyingSituation, tosAcceptedByIp: this.req.ip }, sails.config.custom.verifyEmailAddresses? { emailProofToken: await sails.helpers.strings.random('url-friendly'), @@ -173,7 +160,6 @@ the account verification message.)`, lastName, organization, signupReason, - primaryBuyingSituation, webhookSecret: sails.config.custom.zapierSandboxWebhookSecret, } }) diff --git a/website/api/controllers/entrance/view-login.js b/website/api/controllers/entrance/view-login.js index 0bdc59dfac..cba099dbca 100644 --- a/website/api/controllers/entrance/view-login.js +++ b/website/api/controllers/entrance/view-login.js @@ -26,10 +26,8 @@ module.exports = { if (this.req.me) { if(this.req.me.isSuperAdmin){ throw {redirect: '/admin/generate-license'}; - } else if(this.req.me.hasBillingCard) { - throw {redirect: '/customers/new-license'}; } else { - throw {redirect: '/try-fleet/sandbox'}; + throw {redirect: '/start'}; } } 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/api/controllers/save-questionnaire-progress.js b/website/api/controllers/save-questionnaire-progress.js new file mode 100644 index 0000000000..8ef697de53 --- /dev/null +++ b/website/api/controllers/save-questionnaire-progress.js @@ -0,0 +1,79 @@ +module.exports = { + + + friendlyName: 'Save questionnaire progress and continue', + + + description: 'Saves the user\'s current progress in the get started questionnaire', + + + inputs: { + currentStep: { + type: 'string', + description: 'The step of the get started questionnaire that is being saved.', + isIn: [ + 'start', + 'what-are-you-using-fleet-for', + 'have-you-ever-used-fleet', + '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', + 'managed-cloud-for-growing-deployments', + 'self-hosted-deploy', + ] + }, + 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 = 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)) { + questionnaireProgress = {}; + } else {// other wise clone it from the user record. + questionnaireProgress = _.clone(userRecord.getStartedQuestionnaireAnswers); + } + // 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 + }); + // Set the primary buying situation in the user's session. + this.req.session.primaryBuyingSituation = primaryBuyingSituation; + } + // Set the user's answer to the current step. + questionnaireProgress[currentStep] = formData; + // 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 + }); + // 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 627c688a8a..6951bdbfc1 100644 --- a/website/api/controllers/view-contact.js +++ b/website/api/controllers/view-contact.js @@ -31,7 +31,10 @@ module.exports = { formToShow = 'contact'; } // Respond with view. - return {formToShow}; + 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/controllers/view-start.js b/website/api/controllers/view-start.js index 080e234a09..a18fdd8ed1 100644 --- a/website/api/controllers/view-start.js +++ b/website/api/controllers/view-start.js @@ -17,9 +17,15 @@ module.exports = { fn: async function () { - - // Respond with view. - return {}; + if(this.req.me.lastSubmittedGetStartedQuestionnaireStep && !_.isEmpty(this.req.me.getStartedQuestionnaireAnswers)){ + let currentStep = this.req.me.lastSubmittedGetStartedQuestionnaireStep; + let previouslyAnsweredQuestions = this.req.me.getStartedQuestionnaireAnswers; + // Respond with view. + return {currentStep, previouslyAnsweredQuestions}; + } else { + // Respond with view. + return; + } } 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/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/api/models/User.js b/website/api/models/User.js index 513c4d083e..db66de3d38 100644 --- a/website/api/models/User.js +++ b/website/api/models/User.js @@ -212,6 +212,18 @@ without necessarily having a billing card.` 'mdm', 'vm', ] + }, + + lastSubmittedGetStartedQuestionnaireStep: { + type: 'string', + description: 'The last step the user reached in the get started form.', + defaultsTo: 'start', + }, + + getStartedQuestionnaireAnswers: { + type: 'json', + description: 'This answers the user provided when they filled out the get started form.', + defaultsTo: {}, } // ╔═╗╔╦╗╔╗ ╔═╗╔╦╗╔═╗ 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 0000000000..e7e08af2de Binary files /dev/null and b/website/assets/images/articles/fleet-4.48.0-1600x900@2x.png differ 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 0000000000..0c16f63536 Binary files /dev/null and b/website/assets/images/articles/sysadmin-diaries-1600x900@2x.png differ 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 0000000000..7752817b92 Binary files /dev/null and b/website/assets/images/articles/sysadmin-diaries-change-later-231x160@2x.png differ diff --git a/website/assets/images/articles/sysadmin-diaries-password-policy-updated-689x140@2x.png b/website/assets/images/articles/sysadmin-diaries-password-policy-updated-689x140@2x.png new file mode 100644 index 0000000000..3c02229410 Binary files /dev/null and b/website/assets/images/articles/sysadmin-diaries-password-policy-updated-689x140@2x.png differ diff --git a/website/assets/images/icon-deploy-fleet-64x64@2x.png b/website/assets/images/icon-deploy-fleet-64x64@2x.png new file mode 100644 index 0000000000..f70c0ac9fd Binary files /dev/null and b/website/assets/images/icon-deploy-fleet-64x64@2x.png differ diff --git a/website/assets/images/icon-form-success-12x12@2x.png b/website/assets/images/icon-form-success-12x12@2x.png new file mode 100644 index 0000000000..d09d7c9214 Binary files /dev/null and b/website/assets/images/icon-form-success-12x12@2x.png differ 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 0000000000..8e11b7ad5c Binary files /dev/null and b/website/assets/images/logo-rivian-dark-120x32@2x.png differ diff --git a/website/assets/js/cloud.setup.js b/website/assets/js/cloud.setup.js index 95044bcbb0..cf4a555bd5 100644 --- a/website/assets/js/cloud.setup.js +++ b/website/assets/js/cloud.setup.js @@ -13,7 +13,7 @@ Cloud.setup({ /* eslint-disable */ - methods: {"downloadSitemap":{"verb":"GET","url":"/sitemap.xml","args":[]},"downloadRssFeed":{"verb":"GET","url":"/rss/:categoryName","args":["categoryName"]},"receiveUsageAnalytics":{"verb":"POST","url":"/api/v1/webhooks/receive-usage-analytics","args":["anonymousIdentifier","fleetVersion","licenseTier","numHostsEnrolled","numUsers","numTeams","numPolicies","numLabels","softwareInventoryEnabled","vulnDetectionEnabled","systemUsersEnabled","hostsStatusWebHookEnabled","numWeeklyActiveUsers","numWeeklyPolicyViolationDaysActual","numWeeklyPolicyViolationDaysPossible","hostsEnrolledByOperatingSystem","hostsEnrolledByOrbitVersion","hostsEnrolledByOsqueryVersion","storedErrors","numHostsNotResponding","organization","mdmMacOsEnabled","mdmWindowsEnabled","liveQueryDisabled","hostExpiryEnabled"]},"receiveFromGithub":{"verb":"GET","url":"/api/v1/webhooks/github","args":["botSignature","action","sender","repository","changes","issue","comment","pull_request","label","release"]},"receiveFromStripe":{"verb":"POST","url":"/api/v1/webhooks/receive-from-stripe","args":["id","type","data","webhookSecret"]},"deliverContactFormMessage":{"verb":"POST","url":"/api/v1/deliver-contact-form-message","args":["emailAddress","firstName","lastName","message"]},"sendPasswordRecoveryEmail":{"verb":"POST","url":"/api/v1/entrance/send-password-recovery-email","args":["emailAddress"]},"signup":{"verb":"POST","url":"/api/v1/customers/signup","args":["emailAddress","password","organization","firstName","lastName","signupReason"]},"updateProfile":{"verb":"POST","url":"/api/v1/account/update-profile","args":["firstName","lastName","organization","emailAddress"]},"updatePassword":{"verb":"POST","url":"/api/v1/account/update-password","args":["oldPassword","newPassword"]},"updateBillingCard":{"verb":"POST","url":"/api/v1/account/update-billing-card","args":["stripeToken","billingCardLast4","billingCardBrand","billingCardExpMonth","billingCardExpYear"]},"login":{"verb":"POST","url":"/api/v1/customers/login","args":["emailAddress","password","rememberMe"]},"logout":{"verb":"GET","url":"/api/v1/account/logout","args":[]},"createQuote":{"verb":"POST","url":"/api/v1/customers/create-quote","args":["numberOfHosts"]},"saveBillingInfoAndSubscribe":{"verb":"POST","url":"/api/v1/customers/save-billing-info-and-subscribe","args":["quoteId","organization","firstName","lastName","paymentSource"]},"updatePasswordAndLogin":{"verb":"POST","url":"/api/v1/entrance/update-password-and-login","args":["password","token"]},"deliverDemoSignup":{"verb":"POST","url":"/api/v1/deliver-demo-signup","args":["emailAddress"]},"createOrUpdateOneNewsletterSubscription":{"verb":"POST","url":"/api/v1/create-or-update-one-newsletter-subscription","args":["emailAddress","subscribeTo"]},"unsubscribeFromAllNewsletters":{"verb":"GET","url":"/api/v1/unsubscribe-from-all-newsletters","args":["emailAddress"]},"buildLicenseKey":{"verb":"POST","url":"/api/v1/admin/build-license-key","args":["numberOfHosts","organization","expiresAt","partnerName"]},"createVantaAuthorizationRequest":{"verb":"POST","url":"/api/v1/create-vanta-authorization-request","args":["emailAddress","fleetInstanceUrl","fleetApiKey"]},"deliverMdmBetaSignup":{"verb":"POST","url":"/api/v1/deliver-mdm-beta-signup","args":["emailAddress","fullName","jobTitle","numberOfHosts"]},"deliverAppleCsr":{"verb":"POST","url":"/api/v1/deliver-apple-csr","args":["unsignedCsrData"]},"deliverPremiumUpgradeForm":{"verb":"POST","url":"/api/v1/deliver-premium-upgrade-form","args":["organization","monthsUsingFleetFree","emailAddress","numberOfHosts"]},"deliverLaunchPartySignup":{"verb":"POST","url":"/api/v1/deliver-launch-party-signup","args":["emailAddress","firstName","lastName","jobTitle","phoneNumber"]},"deliverMdmDemoEmail":{"verb":"POST","url":"/api/v1/deliver-mdm-demo-email","args":["emailAddress"]},"provisionSandboxInstanceAndDeliverEmail":{"verb":"POST","url":"/api/v1/admin/provision-sandbox-instance-and-deliver-email","args":["userId"]},"deliverTalkToUsFormSubmission":{"verb":"POST","url":"/api/v1/deliver-talk-to-us-form-submission","args":["emailAddress","firstName","lastName","organization","numberOfHosts","primaryBuyingSituation"]}} + methods: {"downloadSitemap":{"verb":"GET","url":"/sitemap.xml","args":[]},"downloadRssFeed":{"verb":"GET","url":"/rss/:categoryName","args":["categoryName"]},"receiveUsageAnalytics":{"verb":"POST","url":"/api/v1/webhooks/receive-usage-analytics","args":["anonymousIdentifier","fleetVersion","licenseTier","numHostsEnrolled","numUsers","numTeams","numPolicies","numLabels","softwareInventoryEnabled","vulnDetectionEnabled","systemUsersEnabled","hostsStatusWebHookEnabled","numWeeklyActiveUsers","numWeeklyPolicyViolationDaysActual","numWeeklyPolicyViolationDaysPossible","hostsEnrolledByOperatingSystem","hostsEnrolledByOrbitVersion","hostsEnrolledByOsqueryVersion","storedErrors","numHostsNotResponding","organization","mdmMacOsEnabled","mdmWindowsEnabled","liveQueryDisabled","hostExpiryEnabled"]},"receiveFromGithub":{"verb":"GET","url":"/api/v1/webhooks/github","args":["botSignature","action","sender","repository","changes","issue","comment","pull_request","label","release"]},"receiveFromStripe":{"verb":"POST","url":"/api/v1/webhooks/receive-from-stripe","args":["id","type","data","webhookSecret"]},"deliverContactFormMessage":{"verb":"POST","url":"/api/v1/deliver-contact-form-message","args":["emailAddress","firstName","lastName","message"]},"sendPasswordRecoveryEmail":{"verb":"POST","url":"/api/v1/entrance/send-password-recovery-email","args":["emailAddress"]},"signup":{"verb":"POST","url":"/api/v1/customers/signup","args":["emailAddress","password","organization","firstName","lastName","signupReason","primaryBuyingSituation"]},"updateProfile":{"verb":"POST","url":"/api/v1/account/update-profile","args":["firstName","lastName","organization","emailAddress"]},"updatePassword":{"verb":"POST","url":"/api/v1/account/update-password","args":["oldPassword","newPassword"]},"updateBillingCard":{"verb":"POST","url":"/api/v1/account/update-billing-card","args":["stripeToken","billingCardLast4","billingCardBrand","billingCardExpMonth","billingCardExpYear"]},"login":{"verb":"POST","url":"/api/v1/customers/login","args":["emailAddress","password","rememberMe"]},"logout":{"verb":"GET","url":"/api/v1/account/logout","args":[]},"createQuote":{"verb":"POST","url":"/api/v1/customers/create-quote","args":["numberOfHosts"]},"saveBillingInfoAndSubscribe":{"verb":"POST","url":"/api/v1/customers/save-billing-info-and-subscribe","args":["quoteId","organization","firstName","lastName","paymentSource"]},"updatePasswordAndLogin":{"verb":"POST","url":"/api/v1/entrance/update-password-and-login","args":["password","token"]},"deliverDemoSignup":{"verb":"POST","url":"/api/v1/deliver-demo-signup","args":["emailAddress"]},"createOrUpdateOneNewsletterSubscription":{"verb":"POST","url":"/api/v1/create-or-update-one-newsletter-subscription","args":["emailAddress","subscribeTo"]},"unsubscribeFromAllNewsletters":{"verb":"GET","url":"/api/v1/unsubscribe-from-all-newsletters","args":["emailAddress"]},"buildLicenseKey":{"verb":"POST","url":"/api/v1/admin/build-license-key","args":["numberOfHosts","organization","expiresAt","partnerName"]},"createVantaAuthorizationRequest":{"verb":"POST","url":"/api/v1/create-vanta-authorization-request","args":["emailAddress","fleetInstanceUrl","fleetApiKey"]},"deliverMdmBetaSignup":{"verb":"POST","url":"/api/v1/deliver-mdm-beta-signup","args":["emailAddress","fullName","jobTitle","numberOfHosts"]},"deliverAppleCsr":{"verb":"POST","url":"/api/v1/deliver-apple-csr","args":["unsignedCsrData"]},"deliverLaunchPartySignup":{"verb":"POST","url":"/api/v1/deliver-launch-party-signup","args":["emailAddress","firstName","lastName","jobTitle","phoneNumber"]},"deliverMdmDemoEmail":{"verb":"POST","url":"/api/v1/deliver-mdm-demo-email","args":["emailAddress"]},"provisionSandboxInstanceAndDeliverEmail":{"verb":"POST","url":"/api/v1/admin/provision-sandbox-instance-and-deliver-email","args":["userId"]},"deliverTalkToUsFormSubmission":{"verb":"POST","url":"/api/v1/deliver-talk-to-us-form-submission","args":["emailAddress","firstName","lastName","organization","numberOfHosts","primaryBuyingSituation"]},"saveQuestionnaireProgress":{"verb":"POST","url":"/api/v1/save-questionnaire-progress","args":["currentStep","formData"]}} /* eslint-enable */ }); 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/js/pages/contact.page.js b/website/assets/js/pages/contact.page.js index 597b7bd8ce..2b629aa352 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, @@ -32,6 +32,8 @@ parasails.registerPage('contact', { message: {required: false}, }, + formDataToPrefillForLoggedInUsers: {}, + // Server error state for the form cloudError: '', @@ -46,11 +48,22 @@ parasails.registerPage('contact', { if(this.formToShow === 'contact'){ this.formToDisplay = this.formToShow; } + if(this.prefillFormDataFromUserRecord){ + this.formDataToPrefillForLoggedInUsers.emailAddress = this.me.emailAddress; + this.formDataToPrefillForLoggedInUsers.firstName = this.me.firstName; + this.formDataToPrefillForLoggedInUsers.lastName = this.me.lastName; + this.formDataToPrefillForLoggedInUsers.organization = this.me.organization; + // Only prefil this information if the user has this value set. + if(this.me.primaryBuyingSituation) { + this.formDataToPrefillForLoggedInUsers.primaryBuyingSituation = this.me.primaryBuyingSituation; + } + this.formData = _.clone(this.formDataToPrefillForLoggedInUsers); + } if(window.location.search){ window.history.replaceState({}, document.title, '/contact' ); } if(this.primaryBuyingSituation){ - this.audience = this.primaryBuyingSituation; + this.formData.primaryBuyingSituation = this.primaryBuyingSituation;// prefill form } }, mounted: async function() { @@ -78,7 +91,11 @@ parasails.registerPage('contact', { }, clickSwitchForms: function(form) { - this.formData = {}; + if(this.prefillFormDataFromUserRecord){ + this.formData = _.clone(this.formDataToPrefillForLoggedInUsers); + } else { + this.formData = {}; + } this.formErrors = {}; this.cloudError = ''; this.formToDisplay = form; diff --git a/website/assets/js/pages/entrance/signup.page.js b/website/assets/js/pages/entrance/signup.page.js index 1528c7d780..dc9a3a7781 100644 --- a/website/assets/js/pages/entrance/signup.page.js +++ b/website/assets/js/pages/entrance/signup.page.js @@ -17,7 +17,6 @@ parasails.registerPage('signup', { organization: {required: true}, emailAddress: {required: true, isEmail: true}, password: {required: true, minLength: 8}, - primaryBuyingSituation: {required: true}, }, // Syncing / loading state syncing: false, 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/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/js/pages/start.page.js b/website/assets/js/pages/start.page.js index d1db7b5511..9b7173da0b 100644 --- a/website/assets/js/pages/start.page.js +++ b/website/assets/js/pages/start.page.js @@ -3,7 +3,66 @@ parasails.registerPage('start', { // ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣ // ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝ data: { - //… + currentStep: 'start', + + syncing: false, + + // Form data + formData: { + 'start': {stepCompleted: true}, + 'what-are-you-using-fleet-for': {}, + 'have-you-ever-used-fleet': {}, + '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': {stepCompleted: true}, + 'what-did-you-think': {}, + }, + // For tracking client-side validation errors in our form. + // > Has property set to `true` for each invalid property in `formData`. + formErrors: { /* … */ }, + + formRules: {}, + primaryBuyingSituationFormRules: { + primaryBuyingSituation: {required: true} + }, + isUsingFleetFormRules: { + fleetUseStatus: {required: true} + }, + numberOfHostsFormRules: { + numberOfHosts: {required: true} + }, + hostingFleetFormRules: { + willSelfHost: {required: true} + }, + endpointOpsSecurityWorkingOnFormRules: { + endpointOpsSecurityUseCase: {required: true} + }, + endpointOpsItUseCaseFormRules: { + endpointOpsItUseCase: {required: true} + }, + vmUseCaseFormRules: { + vmUseCase: {required: true} + }, + mdmUseCaseFormRules: { + mdmUseCase: {required: true} + }, + endpointOpsSecurityIsItAnyGoodFormRules: { + isItAnyGood: {required: true} + }, + endpointOpsSecurityWhatDidYouThinkFormRules: { + whatDidYouThink: {required: true} + }, + previouslyAnsweredQuestions: {}, + + // Server error state for the forms + cloudError: '', + + // Success state when form has been submitted + cloudSuccess: false, }, // ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗ @@ -11,6 +70,13 @@ parasails.registerPage('start', { // ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝ beforeMount: function() { //… + if(this.currentStep !== 'start'){ + this.prefillPreviousAnswers(); + } + // If this user has not completed the 'what are you using fleet for' step, and has a primaryBuyingSituation set by an ad. prefill the formData for this step. + if(this.primaryBuyingSituation && _.isEmpty(this.formData['what-are-you-using-fleet-for'])){ + this.formData['what-are-you-using-fleet-for'].primaryBuyingSituation = _.clone(this.primaryBuyingSituation); + } }, mounted: async function() { //… @@ -20,6 +86,156 @@ parasails.registerPage('start', { // ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗ // ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝ methods: { - //… + handleSubmittingForm: async function(argins) { + let formDataForThisStep = _.clone(argins); + let nextStep = this.getNextStep(); + let getStartedProgress = await Cloud.saveQuestionnaireProgress.with({ + currentStep: this.currentStep, + formData: formDataForThisStep, + }); + this.previouslyAnsweredQuestions[this.currentStep] = getStartedProgress[this.currentStep]; + this.syncing = false; + this.currentStep = nextStep; + }, + clickGoToPreviousStep: async function() { + switch(this.currentStep) { + case 'have-you-ever-used-fleet': + this.currentStep = 'what-are-you-using-fleet-for'; + break; + case 'how-many-hosts': + this.currentStep = 'have-you-ever-used-fleet'; + break; + case 'will-you-be-self-hosting': + this.currentStep = 'how-many-hosts'; + break; + case 'self-hosted-deploy': + this.currentStep = 'will-you-be-self-hosting'; + break; + case 'managed-cloud-for-growing-deployments': + this.currentStep = 'will-you-be-self-hosting'; + break; + case 'what-are-you-working-on-eo-security': + this.currentStep = 'have-you-ever-used-fleet'; + break; + case 'is-it-any-good': + let primaryBuyingSituation = this.formData['what-are-you-using-fleet-for'].primaryBuyingSituation; + if(primaryBuyingSituation === 'eo-security'){ + this.currentStep = 'what-are-you-working-on-eo-security'; + } else if(primaryBuyingSituation === 'eo-it') { + this.currentStep = 'what-does-your-team-manage-eo-it'; + } else if(primaryBuyingSituation === 'vm') { + this.currentStep = 'what-does-your-team-manage-vm'; + } else if(primaryBuyingSituation === 'mdm') { + this.currentStep = 'what-do-you-manage-mdm'; + } + break; + case 'lets-talk-to-your-team': + this.currentStep = 'how-many-hosts'; + break; + case 'welcome-to-fleet': + this.currentStep = 'have-you-ever-used-fleet'; + break; + case 'deploy-fleet-in-your-environment': + this.currentStep = 'what-did-you-think'; + break; + case 'what-did-you-think': + this.currentStep = 'is-it-any-good'; + break; + case 'what-does-your-team-manage-eo-it': + this.currentStep = 'have-you-ever-used-fleet'; + break; + case 'what-does-your-team-manage-vm': + this.currentStep = 'have-you-ever-used-fleet'; + break; + case 'what-do-you-manage-mdm': + this.currentStep = 'have-you-ever-used-fleet'; + break; + } + }, + getNextStep: function() { + let nextStepInForm; + switch(this.currentStep) { + case 'start': + nextStepInForm = 'what-are-you-using-fleet-for'; + break; + case 'what-are-you-using-fleet-for': + nextStepInForm = 'have-you-ever-used-fleet'; + break; + case 'have-you-ever-used-fleet': + let fleetUseStatus = this.formData['have-you-ever-used-fleet'].fleetUseStatus; + let primaryBuyingSituation = this.formData['what-are-you-using-fleet-for'].primaryBuyingSituation; + if(fleetUseStatus === 'yes-recently-deployed' || fleetUseStatus === 'yes-deployed') { + nextStepInForm = 'how-many-hosts'; + } else { + if(primaryBuyingSituation === 'eo-security'){ + nextStepInForm = 'what-are-you-working-on-eo-security'; + } else if(primaryBuyingSituation === 'eo-it') { + nextStepInForm = 'what-does-your-team-manage-eo-it'; + } else if(primaryBuyingSituation === 'vm') { + nextStepInForm = 'what-does-your-team-manage-vm'; + } else if(primaryBuyingSituation === 'mdm') { + nextStepInForm = 'what-do-you-manage-mdm'; + } + } + break; + case 'how-many-hosts': + if(this.formData['how-many-hosts'].numberOfHosts === '1-100' || + this.formData['how-many-hosts'].numberOfHosts === '100-700') { + nextStepInForm = 'will-you-be-self-hosting'; + } else { + nextStepInForm = 'lets-talk-to-your-team'; + } + break; + case 'will-you-be-self-hosting': + if(this.formData['will-you-be-self-hosting'].willSelfHost === 'true'){ + nextStepInForm = 'self-hosted-deploy'; + } else { + nextStepInForm = 'managed-cloud-for-growing-deployments'; + } + break; + case 'what-are-you-working-on-eo-security': + nextStepInForm = 'is-it-any-good'; + break; + case 'what-does-your-team-manage-eo-it': + nextStepInForm = 'is-it-any-good'; + break; + case 'what-does-your-team-manage-vm': + nextStepInForm = 'is-it-any-good'; + break; + case 'what-do-you-manage-mdm': + nextStepInForm = 'is-it-any-good'; + break; + case 'is-it-any-good': + nextStepInForm = 'what-did-you-think'; + break; + case 'what-did-you-think': + if(this.formData['what-did-you-think'].whatDidYouThink === 'let-me-think-about-it'){ + nextStepInForm = 'is-it-any-good'; + } else { + nextStepInForm = 'deploy-fleet-in-your-environment'; + } + break; + } + return nextStepInForm; + }, + clickGoToCalendly: function() { + window.location = `https://calendly.com/fleetdm/talk-to-us?email=${encodeURIComponent(this.me.emailAddress)}&name=${encodeURIComponent(this.me.firstName+' '+this.me.lastName)}`; + }, + clickGoToContactPage: function() { + window.location = `/contact?prefillFormDataFromUserRecord`; + }, + clickClearOneFormError: function(field) { + if(this.formErrors[field]){ + this.formErrors = _.omit(this.formErrors, field); + } + }, + prefillPreviousAnswers: function() { + if(!_.isEmpty(this.previouslyAnsweredQuestions)){ + for(let step in this.previouslyAnsweredQuestions){ + this.formData[step] = this.previouslyAnsweredQuestions[step]; + } + this.currentStep = this.getNextStep(); + } + }, } }); 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; + } } } 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/contact.less b/website/assets/styles/pages/contact.less index b9d42ef501..be452264a8 100644 --- a/website/assets/styles/pages/contact.less +++ b/website/assets/styles/pages/contact.less @@ -137,7 +137,6 @@ } [purpose='quote'] { display: flex; - min-height: 300px; padding: 32px; flex-direction: column; justify-content: center; diff --git a/website/assets/styles/pages/fleetctl-preview.less b/website/assets/styles/pages/fleetctl-preview.less index 7d1973f9f7..2cc274e8f6 100644 --- a/website/assets/styles/pages/fleetctl-preview.less +++ b/website/assets/styles/pages/fleetctl-preview.less @@ -49,6 +49,11 @@ margin-bottom: 24px; } } + [purpose='contact-link'] { + color: @core-fleet-black-75; + text-decoration: underline; + text-underline-offset: 3.5px; + } [purpose='platform-selector'] { margin-bottom: 60px; 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/assets/styles/pages/start.less b/website/assets/styles/pages/start.less index 2ab70735ef..e1709537bc 100644 --- a/website/assets/styles/pages/start.less +++ b/website/assets/styles/pages/start.less @@ -8,18 +8,152 @@ font-weight: 800; line-height: 150%; } + h2 { + margin-bottom: 32px; + font-size: 24px; + font-weight: 800; + line-height: 120%; + } [purpose='logo-container'] { max-width: 524px; margin-left: auto; margin-right: auto; } + [purpose='form-header'] { + margin-bottom: 32px; + h2 { + margin-bottom: 12px; + } + p { + margin-bottom: 0px; + } + } + [purpose='form-container'] { + width: 528px; + margin-left: auto; + margin-right: auto; + } [purpose='page-container'] { - padding-top: 80px; + padding-top: 64px; padding-left: 64px; padding-right: 64px; + padding-bottom: 64px; max-width: unset; display: flex; flex-direction: column; + justify-content: center; + } + [purpose='progress-bar-container'] { + display: flex; + flex-direction: row; + align-items: center; + margin-bottom: 32px; + img { + height: 18px; + display: inline; + margin-left: 10px; + } + } + [purpose='form-progress-bar'] { + height: 6px; + width: 200px; + background: #E2E4EA; + border-radius: 3px; + [purpose='current-progress'] { + background-color: #3DB67B; + height: 6px; + border-radius: 3px; + } + } + .form-group.is-invalid { + color: @core-vibrant-red; + .form-control { + color: @core-vibrant-red; + } + .invalid-feedback { + display: block; + } + } + [purpose='form-option'] { + user-select: none; + cursor: pointer; + width: fit-content; + padding: 8px 12px 8px 8px; + margin-bottom: 16px; + display: flex; + flex-direction: row; + align-items: center; + border-radius: 7px; + border: 1px solid #E2E4EA; + font-size: 16px; + line-height: 24px; + color: #515774; + white-space: nowrap; + height: fit-content; + input { + cursor: pointer; + margin-right: 8px; + display: none; + } + [purpose='custom-radio'] { + margin-right: 8px; + display: flex; + min-width: 18px; + min-height: 18px; + border-radius: 50%; + border: 1px solid #E2E4EA; + justify-content: center; + align-items: center; + [purpose='custom-radio-selected'] { + min-width: 10px; + min-height: 10px; + border-radius: 50%; + background-color: @core-vibrant-blue; + transform: scale(0); + transition: 180ms transform ease-in-out; + } + } + input[type='radio']:checked + [purpose='custom-radio'] { + [purpose='custom-radio-selected'] { + transform: scale(1); + } + } + .form-control { + height: 40px; + } + &:hover { + border: 1px solid @core-vibrant-blue; + } + &.selected { + border: 1px solid @core-vibrant-blue; + } + } + + [purpose='contact-link'] { + text-decoration: underline; + text-underline-offset: 3.5px; + } + [purpose='form-buttons'] { + margin-top: 32px; + display: flex; + flex-direction: row; + align-items: center; + justify-content: start; + user-select: none; + } + [purpose='submit-button'] { + padding: 12px; + font-size: 14px; + font-weight: 700; + line-height: 150%; + margin-right: 24px; + } + [purpose='back-button'] { + cursor: pointer; + font-size: 14px; + font-weight: 700; + line-height: 150%; + margin-right: 24px; } [purpose='start-cards'] { display: flex; @@ -31,13 +165,14 @@ } [purpose='card'] { width: 252px; + height: 200px; display: flex; flex-direction: column; justify-content: center; align-items: center; text-decoration: none; - - padding: 43px 52px; + cursor: pointer; + padding: 24px; background: #FFF; color: @core-fleet-black-75; border-radius: 12px; @@ -57,7 +192,7 @@ font-size: 12px; font-weight: 400; line-height: 150%; - white-space: nowrap; + // white-space: nowrap; margin-bottom: 0px; } &:first-of-type { @@ -68,6 +203,50 @@ } } + [purpose='quote'] { + display: flex; + padding: 32px; + flex-direction: column; + justify-content: center; + align-items: flex-start; + align-self: stretch; + border-radius: 16px; + background: #FFF; + box-shadow: none; + background-color: #FFF; + height: fit-content; + max-width: 100%; + text-align: left; + border: 1px solid @core-vibrant-blue-15; + [purpose='logo'] { + img { + max-height: 32px; + } + min-height: 0; + margin-bottom: 24px; + } + [purpose='quote-text'] { + margin-bottom: 24px; + font-size: 14px; + line-height: 150%; + } + [purpose='quote-author-info'] { + [purpose='job-title'] { + font-size: 12px; + line-height: 18px; + } + [purpose='name'] { + color: @core-fleet-black; + } + [purpose='profile-picture'] { + margin-right: 16px; + img { + height: 48px; + width: 48px; + } + } + } + } @media (max-width: 991px) { [purpose='page-container'] { padding-top: 60px; @@ -91,11 +270,23 @@ } [purpose='start-cards'] { flex-direction: column; + [purpose='card'] { + width: 100%; + } } [purpose='card']:first-of-type { margin-right: unset; margin-bottom: 20px; } + [purpose='form-container'] { + width: unset; + margin-left: 0; + margin-right: 0; + } + [purpose='form-option'] { + white-space: unset; + width: unset; + } } @media (max-width: 375px) { h1 { 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/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 6a486f1db6..d1d490ffc7 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', } }, @@ -280,44 +280,10 @@ module.exports.routes = { 'GET /start': { action: 'view-start', locals: { - hideHeaderLinks: true, hideFooterLinks: true, + hideGetStartedButton: true, pageTitleForMeta: 'Start | Fleet', - pageDescriptionForMeta: 'Get Started with Fleet. Spin up a local demo or get your premium license key.', - } - }, - - - // ╦╔╦╗╔═╗╔═╗╦╔╗╔╔═╗ ┌─┬ ┌─┐┌┐┌┌┬┐┬┌┐┌┌─┐ ┌─┐┌─┐┌─┐┌─┐┌─┐─┐ - // ║║║║╠═╣║ ╦║║║║║╣ │ │ ├─┤│││ │││││││ ┬ ├─┘├─┤│ ┬├┤ └─┐ │ - // ╩╩ ╩╩ ╩╚═╝╩╝╚╝╚═╝ └─┴─┘┴ ┴┘└┘─┴┘┴┘└┘└─┘ ┴ ┴ ┴└─┘└─┘└─┘─┘ - '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.', + pageDescriptionForMeta: 'Get Started with Fleet. Spin up a local demo or get your Premium license key.', } }, @@ -512,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 // ============================================================================================================= @@ -573,8 +543,8 @@ 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' }, + 'POST /api/v1/save-questionnaire-progress': { action: 'save-questionnaire-progress' }, }; diff --git a/website/generators/landing-page/index.js b/website/generators/landing-page/index.js index 5199522bb4..2ed7891caa 100644 --- a/website/generators/landing-page/index.js +++ b/website/generators/landing-page/index.js @@ -219,8 +219,8 @@ module.exports = {

${_.capitalize(scope.stem.replace(/\-/gim, ' '))}

Vitae architecto reiciendis in temporibus consequatur doloremque reprehenderit perferendis? Eaque quod voluptates earum corporis, quo labore reprehenderit libero sint.

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

Open-source device management

Lighter than air

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+'`.**'; } 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', 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/contact.ejs b/website/views/pages/contact.ejs index d7bcf17d6a..fc70448c1f 100644 --- a/website/views/pages/contact.ejs +++ b/website/views/pages/contact.ejs @@ -79,8 +79,8 @@
@@ -88,10 +88,14 @@
Please select an option.
- + -

Includes computers, servers, containers, and other hosts.

-
Please enter a number of devices
+

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' %>

Please enter a valid work email address

@@ -108,7 +112,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 +127,7 @@

-
+
Uber logo

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

-
+
+
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.

@@ -152,7 +157,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 +172,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. diff --git a/website/views/pages/device-management.ejs b/website/views/pages/device-management.ejs index aa0a3eb1de..b0529e7bb1 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.

@@ -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

@@ -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..875048fb54 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.

@@ -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).

@@ -183,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

@@ -194,8 +203,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/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/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/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 67272edc1e..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 599003ccb9..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 0f4b5e19e5..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 c31434487e..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() %> 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/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/start.ejs b/website/views/pages/start.ejs index e48d94933c..0b5b407a3f 100644 --- a/website/views/pages/start.ejs +++ b/website/views/pages/start.ejs @@ -1,23 +1,664 @@
-
-

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 +
+
+
+ <%// ┬ ┬┌─┐┬ ┬ ┌┬┐┌─┐┌┐┌┬ ┬ ┬ ┬┌─┐┌─┐┌┬┐┌─┐ + // ├─┤│ ││││ │││├─┤│││└┬┘ ├─┤│ │└─┐ │ └─┐ + // ┴ ┴└─┘└┴┘ ┴ ┴┴ ┴┘└┘ ┴ ┴ ┴└─┘└─┘ ┴ └─┘%> +
+
+
+ 🏆 +
+
+

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.

+
+ +
+ +
+ + + + +
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 +
+
+
+ <%// ┬ ┬┬ ┬┌─┐┌┬┐ ┌┬┐┌─┐┌─┐┌─┐ ┬ ┬┌─┐┬ ┬┬─┐ ┌┬┐┌─┐┌─┐┌┬┐ ┌┬┐┌─┐┌┐┌┌─┐┌─┐┌─┐ + // │││├─┤├─┤ │ │││ │├┤ └─┐ └┬┘│ ││ │├┬┘ │ ├┤ ├─┤│││ │││├─┤│││├─┤│ ┬├┤ + // └┴┘┴ ┴┴ ┴ ┴ ─┴┘└─┘└─┘└─┘ ┴ └─┘└─┘┴└─ ┴ └─┘┴ ┴┴ ┴ ┴ ┴┴ ┴┘└┘┴ ┴└─┘└─┘ + // ┌─┐┌─┐ ┬┌┬┐ + // ├┤ │ │───│ │ + // └─┘└─┘ ┴ ┴%> +
+
+
+ 🏆 +
+

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 +
+
+
+ <%// ┬┌─┐ ┬┌┬┐ ┌─┐┌┐┌┬ ┬ ┌─┐┌─┐┌─┐┌┬┐ + // │└─┐ │ │ ├─┤│││└┬┘ │ ┬│ ││ │ ││ + // ┴└─┘ ┴ ┴ ┴ ┴┘└┘ ┴ └─┘└─┘└─┘─┴┘%> +
+ +
+
+ 🏆 +
+

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.

+
+ +

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

+
+
+
+
+
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. +

+
+
+ 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/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 6971d60fe4..b60dee57ec 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.

@@ -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

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

Open-source vulnerability management

Build the vulnerability program you actually want

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"