diff --git a/.github/ISSUE_TEMPLATE/release-qa.md b/.github/ISSUE_TEMPLATE/release-qa.md index 0e31bb1b5a52..b29885ce1fe8 100644 --- a/.github/ISSUE_TEMPLATE/release-qa.md +++ b/.github/ISSUE_TEMPLATE/release-qa.md @@ -2,7 +2,7 @@ name: Release QA about: Checklist of required tests prior to release title: 'Release QA:' -labels: '#g-mdm,#g-endpoint-ops,:release' +labels: '#g-mdm,#g-orchestration,#g-software:release' assignees: 'xpkoala,pezhub,jmwatts' --- @@ -27,11 +27,12 @@ Smoke tests are limited to core functionality and serve as a pre-release final r ### Prerequisites -1. `fleetctl preview` is set up and running the desired test version using [`--tag` parameters.](https://github.com/fleetdm/fleet/blob/main/handbook/product.md#manual-qa ) +1. `fleetctl preview` is set up and running the desired test version using [`--tag` parameters.](https://fleetdm.com/handbook/engineering#run-fleet-locally-for-qa-purposes) 2. Unless you are explicitly testing older browser versions, browser is up to date. 3. Certificate & flagfile are in place to create new host. 4. In your browser, clear local storage using devtools. +### Orchestration @@ -53,20 +54,6 @@ Smoke tests are limited to core functionality and serve as a pre-release final r 3. forget password link prompts for email 4. valid credentials result in a successful login. 5. valid sso credentials result in a successful login - - - + +
Test nameStep instructionsExpected resultpass/fail
$Name{what a tester should do}{what a tester should see when they do that}pass/fail
pass/fail
Query flowCreate, edit, run, and delete queries. - -1. permissions regarding creating/editing/deleting queries are up to date with documentation -2. syntax errors result in error messaging -3. queries can be run manually -pass/fail
Host FlowVerify a new host can be added and removed following modal instructions using your own device. - -1. Host is added via command line -2. Host serial number and date added are accurate -3. Host is not visible after it is deleted -4. Warning and informational modals show when expected and make sense -pass/fail
Packs flowVerify management, operation, and loggingΒ of ["2017 packs"](https://fleetdm.com/handbook/company/why-this-way#why-does-fleet-support-query-packs). 1. Packs successfully run on host machines after migrations @@ -82,16 +69,17 @@ Smoke tests are limited to core functionality and serve as a pre-release final r 2. Software, query, policy, and packs logs are successfully sent to Filesystem log destinations pass/fail
OS settingsVerify OS settings functionality - -
My device pageVerify the end user's my device page loads successfully. - -1. Clicking the Fleet desktop item, then "My device" successfully loads the my device page. -2. The "My device" page is populated correctly and as expected. -3. Styling and padding appears correct. - +1. Verify able to configure Disk encryption (macOS, Windows, & Linux). +2. Verify host enrolled with Disk encryption enforced successfully encrypts. pass/fail
+### MDM + + + - - - - - +
Test nameStep instructionsExpected resultpass/fail
$Name{what a tester should do}{what a tester should see when they do that}pass/fail
MDM enrollment flowVerify MDM enrollments, run MDM commands 1. Erase an ADE-eligible macOS host and verify able to complete automated enrollment flow. @@ -106,45 +94,22 @@ Smoke tests are limited to core functionality and serve as a pre-release final r 3. Turn off MDM on a non ADE-eligible macOS host. 4. On the My device page, follow the "Turn on MDM" instructions and verify that MDM is turned on. pass/fail
ScriptsVerify script library and execution - -1. Verify able to run a script on all host types from CLI. -2. Verify scripts library upload/download/delete. -3. From Host details (macOS, Windows, & Linux) run a script that should PASS, verify. -4. From Host details (macOS, Windows, & Linux) run a script that should FAIL, verify. -5. Verify UI loading state and statuses for scripts. -6. Disable scripts globally and verify unable to run. -7. Verify scripts display correctly in Activity feed. -pass/fail
SoftwareVerify software library and install / download - -1. Verify software library upload/download/delete. -2. From Host details (macOS, Windows, & Linux) run an install that should PASS, verify. -3. From My Device (macOS, Windows, & Linux) software tab should have self-service items available, verify. -4. Verify UI loading state and statuses for installing software. -6. Verify software installs display correctly in Activity feed. -pass/fail
OS settingsVerify OS settings functionality -1. Verify able to configure Disk encryption (macOS, Windows, & Linux). -2. Verify host enrolled with Disk encryption enforced successfully encrypts. -3. Verify Profiles upload/download/delete (macOS & Windows). -4. Verify Profiles are delivered to host and applied. +1. Verify Profiles upload/download/delete (macOS & Windows). +2. Verify Profiles are delivered to host and applied. pass/fail
Setup experienceVerify macOS Setup experience 1. Configure End user authentication. -2. Upload a Bootstrap package. -3. Add software (FMA, VPP, & Custom pkg) -4. Add a script -5. Enroll an ADE-eligible macOS host and verify successful authentication. -6. Verify Bootstrap package is delivered. -7. Verify SwiftDialogue window displays. -8. Verify software installs and script runs. +3. Upload a Bootstrap package. +4. Add software (FMA, VPP, & Custom pkg) +5. Add a script +6. Enroll an ADE-eligible macOS host and verify successful authentication. +7. Verify Bootstrap package is delivered. +8. Verify SwiftDialogue window displays. +9. Verify software installs and script runs. pass/fail
OS updatesVerify OS updates flow @@ -161,6 +126,7 @@ Smoke tests are limited to core functionality and serve as a pre-release final r 3. Verify Profiles are delivered to host and applied. 4. Verify VPP apps install & display correctly in Activity feed. +pass/fail
Certificates UploadAPNs cert and ABM token renewal workflow 1. Renew APNs Certificate. @@ -168,18 +134,70 @@ Smoke tests are limited to core functionality and serve as a pre-release final r 3. Ensure ADE hosts can enroll. pass/fail
+ +### Software + + + + + + + + + + + - +
Test nameStep instructionsExpected resultpass/fail
$Name{what a tester should do}{what a tester should see when they do that}pass/fail
Query flowCreate, edit, run, and delete queries. + +1. permissions regarding creating/editing/deleting queries are up to date with documentation +2. syntax errors result in error messaging +3. queries can be run manually +pass/fail
Host FlowVerify a new host can be added and removed following modal instructions using your own device. + +1. Host is added via command line +2. Host serial number and date added are accurate +3. Host is not visible after it is deleted +4. Warning and informational modals show when expected and make sense +pass/fail
My device pageVerify the end user's my device page loads successfully. + +1. Clicking the Fleet desktop item, then "My device" successfully loads the my device page. +2. The "My device" page is populated correctly and as expected. +3. Styling and padding appears correct. + +pass/fail
ScriptsVerify script library and execution + +1. Verify able to run a script on all host types from CLI. +2. Verify scripts library upload/download/delete. +3. From Host details (macOS, Windows, & Linux) run a script that should PASS, verify. +4. From Host details (macOS, Windows, & Linux) run a script that should FAIL, verify. +5. Verify UI loading state and statuses for scripts. +8. Disable scripts globally and verify unable to run. +9. Verify scripts display correctly in Activity feed. +pass/fail
SoftwareVerify software library and install / download + +1. Verify software library upload/download/delete. +2. From Host details (macOS, Windows, & Linux) run an install that should PASS, verify. +3. From My Device (macOS, Windows, & Linux) software tab should have self-service items available, verify. +4. Verify UI loading state and statuses for installing software. +7. Verify software installs display correctly in Activity feed. +pass/fail
Migration TestVerify Fleet can migrate to the next version with no issues. Using the migration scripts located in fleet/test/upgrade/ 1. Run the upgrade_test.go script using the most recent stable version of Fleet and `main`. 2. Upgrade test returns an 'OK' response. pass/fail
+ +### All Product Groups + + + +
Test nameStep instructionsExpected resultpass/fail
$Name{what a tester should do}{what a tester should see when they do that}pass/fail
Release blockersVerify there are no outstanding release blocking tickets. 1. Check [this](https://github.com/fleetdm/fleet/labels/~release%20blocker) filter to view all open `~release blocker` tickets. 2. If any are found raise an alarm in the `#help-engineering` and `#g-mdm` (or `#g-endpoint-ops`) channels. -pass/fail
pass/fail
### Notes diff --git a/.github/workflows/build-binaries.yaml b/.github/workflows/build-binaries.yaml index 8499171c1583..52110b2a0cd0 100644 --- a/.github/workflows/build-binaries.yaml +++ b/.github/workflows/build-binaries.yaml @@ -88,7 +88,7 @@ jobs: - name: Build binaries run: make - - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v2 + - uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: build path: build/ diff --git a/.github/workflows/build-fleetd-base-pkg.yml b/.github/workflows/build-fleetd-base-pkg.yml index 8a1ba103897f..6a72383fe4ea 100644 --- a/.github/workflows/build-fleetd-base-pkg.yml +++ b/.github/workflows/build-fleetd-base-pkg.yml @@ -107,14 +107,14 @@ jobs: ' > fleetd-base-manifest.plist - name: Upload fleetd-base.pkg - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v2 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: fleetd-base.pkg path: | fleetd-base.pkg - name: Upload fleetd-base-manifest.plist - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v2 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: fleetd-base-manifest.plist path: | diff --git a/.github/workflows/fleet-and-orbit.yml b/.github/workflows/fleet-and-orbit.yml index 2dcdd6e87e35..887ac1dbceda 100644 --- a/.github/workflows/fleet-and-orbit.yml +++ b/.github/workflows/fleet-and-orbit.yml @@ -155,7 +155,7 @@ jobs: - name: Upload fleet logs if: always() - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v2 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: fleet-logs path: | @@ -163,7 +163,7 @@ jobs: - name: Upload cloudflared logs if: always() - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v2 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: cloudflared.log path: cloudflared.log @@ -235,7 +235,7 @@ jobs: make osqueryd-app-tar-gz version=$OSQUERY_VERSION out-path=. - name: Upload desktop.app.tar.gz and osqueryd.app.tar.gz - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v2 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: macos-pre-built-apps path: | @@ -274,7 +274,7 @@ jobs: - name: Download macos pre-built apps id: download - uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741 # v2 + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: macos-pre-built-apps @@ -301,21 +301,21 @@ jobs: ./tools/tuf/test/main.sh - name: Upload PKG installer - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v2 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: fleet-osquery.pkg path: | fleet-osquery.pkg - name: Upload DEB installer - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v2 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: fleet-osquery_42.0.0_amd64.deb path: | fleet-osquery_42.0.0_amd64.deb - name: Upload MSI installer - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v2 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: fleet-osquery.msi path: | @@ -336,7 +336,7 @@ jobs: - name: Download pkg id: download - uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741 # v2 + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: fleet-osquery.pkg @@ -365,9 +365,9 @@ jobs: - name: Upload orbit logs if: always() - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v2 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: - name: orbit-logs + name: orbit-logs-macos path: | orbit-logs @@ -387,7 +387,7 @@ jobs: - name: Download deb id: download - uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741 # v2 + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: fleet-osquery_42.0.0_amd64.deb @@ -416,9 +416,9 @@ jobs: - name: Upload orbit logs if: always() - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v2 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: - name: orbit-logs + name: orbit-logs-ubuntu path: | orbit-logs @@ -438,7 +438,7 @@ jobs: - name: Download msi id: download - uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741 # v2 + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: fleet-osquery.msi @@ -535,7 +535,7 @@ jobs: - name: Upload Orbit logs if: always() - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v2 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: orbit-logs-windows path: C:\Windows\system32\config\systemprofile\AppData\Local\FleetDM\Orbit\Logs\orbit-osquery.log diff --git a/.github/workflows/fleetctl-preview-latest.yml b/.github/workflows/fleetctl-preview-latest.yml index 630cfd1dc325..2d42cd21fce7 100644 --- a/.github/workflows/fleetctl-preview-latest.yml +++ b/.github/workflows/fleetctl-preview-latest.yml @@ -94,7 +94,7 @@ jobs: - name: Upload logs if: always() - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v2 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: ${{ matrix.os }}-log path: | diff --git a/.github/workflows/fleetctl-preview.yml b/.github/workflows/fleetctl-preview.yml index ab9a69c2b284..19a5d50c842a 100644 --- a/.github/workflows/fleetctl-preview.yml +++ b/.github/workflows/fleetctl-preview.yml @@ -55,7 +55,7 @@ jobs: - name: Upload logs if: always() - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v2 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: ${{ matrix.os }}-log path: | diff --git a/.github/workflows/generate-nudge-targets.yml b/.github/workflows/generate-nudge-targets.yml index 4b7f025f4ec7..a07fcadd50ba 100644 --- a/.github/workflows/generate-nudge-targets.yml +++ b/.github/workflows/generate-nudge-targets.yml @@ -45,7 +45,7 @@ jobs: run: make nudge-app-tar-gz version=$NUDGE_VERSION out-path=. - name: Upload nudge.app.tar.gz - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v2 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: nudge.app.tar.gz path: nudge.app.tar.gz diff --git a/.github/workflows/generate-osqueryd-targets.yml b/.github/workflows/generate-osqueryd-targets.yml index 4d6fc61e69cb..2a5c537dec8d 100644 --- a/.github/workflows/generate-osqueryd-targets.yml +++ b/.github/workflows/generate-osqueryd-targets.yml @@ -55,7 +55,7 @@ jobs: subject-path: "osqueryd.app.tar.gz" - name: Upload osqueryd.app.tar.gz - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v2 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: osqueryd.app.tar.gz path: osqueryd.app.tar.gz @@ -86,7 +86,7 @@ jobs: subject-path: "opt/osquery/bin/osqueryd" - name: Upload osqueryd for linux - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v2 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: osqueryd path: opt/osquery/bin/osqueryd @@ -121,7 +121,7 @@ jobs: subject-path: "opt/osquery/bin/osqueryd" - name: Upload osqueryd for linux-arm64 - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v2 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: osqueryd-arm64 path: opt/osquery/bin/osqueryd @@ -154,7 +154,7 @@ jobs: subject-path: C:\temp\osquery\osqueryd\osqueryd.exe - name: Upload osqueryd for Windows - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v2 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: osqueryd.exe path: C:\temp\osquery\osqueryd\osqueryd.exe diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 98c9cd3a5973..bdde3fc27534 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -134,7 +134,7 @@ jobs: - name: Upload cloudflared logs if: always() - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v2 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: cloudflared.log path: cloudflared.log @@ -230,7 +230,7 @@ jobs: - name: Upload Orbit logs if: always() - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v2 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: orbit-macos-${{ matrix.orbit-channel }}-${{ matrix.osqueryd-channel }}-${{ matrix.desktop-channel }}-logs path: | @@ -300,7 +300,7 @@ jobs: - name: Upload Orbit logs if: always() - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v2 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: orbit-ubuntu-${{ matrix.orbit-channel }}-${{ matrix.osqueryd-channel }}-${{ matrix.desktop-channel }}-logs path: | @@ -346,7 +346,7 @@ jobs: mv fleet-osquery.msi orbit-${{ matrix.orbit-channel }}-osqueryd-${{ matrix.osqueryd-channel }}-desktop-${{ matrix.desktop-channel }}.msi - name: Upload MSI - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v2 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: orbit-${{ matrix.orbit-channel }}-osqueryd-${{ matrix.osqueryd-channel }}-desktop-${{ matrix.desktop-channel }}.msi path: orbit-${{ matrix.orbit-channel }}-osqueryd-${{ matrix.osqueryd-channel }}-desktop-${{ matrix.desktop-channel }}.msi @@ -378,7 +378,7 @@ jobs: - name: Download MSI id: download - uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741 # v2 + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: orbit-${{ matrix.orbit-channel }}-osqueryd-${{ matrix.osqueryd-channel }}-desktop-${{ matrix.desktop-channel }}.msi @@ -394,14 +394,14 @@ jobs: - name: Upload orbit install log if: always() - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v2 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: msiexec-install-log path: log.txt - name: Upload Orbit logs if: always() - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v2 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: orbit-windows-${{ matrix.orbit-channel }}-${{ matrix.osqueryd-channel }}-${{ matrix.desktop-channel }}-logs path: C:\Windows\system32\config\systemprofile\AppData\Local\FleetDM\Orbit\Logs\orbit-osquery.log diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index 59dfd8a7ff3f..c1fba8fcd840 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -44,7 +44,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: SARIF file path: results.sarif diff --git a/.github/workflows/test-go.yaml b/.github/workflows/test-go.yaml index 75c480092854..e347f7278217 100644 --- a/.github/workflows/test-go.yaml +++ b/.github/workflows/test-go.yaml @@ -42,7 +42,7 @@ jobs: test-go: strategy: matrix: - suite: ["integration", "core"] + suite: ["integration", "core", "mysql", "fleetctl", "vuln"] os: [ubuntu-latest] mysql: ["mysql:8.0.36", "mysql:8.4.3", "mysql:9.1.0"] # make sure to update supported versions docs when this changes isCron: @@ -118,10 +118,13 @@ jobs: - name: Run Go Tests run: | if [[ "${{ matrix.suite }}" == "core" ]]; then + CI_TEST_PKG=main RUN_TESTS_ARG='-skip=^TestIntegrations' elif [[ "${{ matrix.suite }}" == "integration" ]]; then + CI_TEST_PKG=main RUN_TESTS_ARG='-run=^TestIntegrations' else + CI_TEST_PKG="${{ matrix.suite }}" RUN_TESTS_ARG='' fi GO_TEST_EXTRA_FLAGS="-v -race=$RACE_ENABLED -timeout=$GO_TEST_TIMEOUT $RUN_TESTS_ARG" \ @@ -135,6 +138,7 @@ jobs: SAML_IDP_TEST=1 \ MAIL_TEST=1 \ NETWORK_TEST_GITHUB_TOKEN=${{ secrets.FLEET_RELEASE_GITHUB_PAT }} \ + CI_TEST_PKG="${CI_TEST_PKG}" \ make test-go 2>&1 | tee /tmp/gotest.log - name: Create mysql identifier without colon diff --git a/Makefile b/Makefile index 406d8486bf79..6ffdca38de79 100644 --- a/Makefile +++ b/Makefile @@ -150,14 +150,30 @@ dump-test-schema: # TESTS_TO_RUN: Name specific tests to run in the specified packages. Leave blank to run all tests in the specified packages. # GO_TEST_EXTRA_FLAGS: Used to specify other arguments to `go test`. # GO_TEST_MAKE_FLAGS: Internal var used by other targets to add arguments to `go test`. -# +# PKG_TO_TEST := "" # default to empty string; can be overridden on command line. go_test_pkg_to_test := $(addprefix ./,$(PKG_TO_TEST)) # set paths for packages to test dlv_test_pkg_to_test := $(addprefix github.com/fleetdm/fleet/v4/,$(PKG_TO_TEST)) # set URIs for packages to debug +DEFAULT_PKG_TO_TEST := ./cmd/... ./ee/... ./orbit/pkg/... ./orbit/cmd/orbit ./pkg/... ./server/... ./tools/... +ifeq ($(CI_TEST_PKG), main) + CI_PKG_TO_TEST=$(shell go list ${DEFAULT_PKG_TO_TEST} | grep -v "server/datastore/mysql" | grep -v "cmd/fleetctl" | grep -v "server/vulnerabilities" | sed -e 's|github.com/fleetdm/fleet/v4/||g') +else ifeq ($(CI_TEST_PKG), mysql) + CI_PKG_TO_TEST="server/datastore/mysql/..." +else ifeq ($(CI_TEST_PKG), fleetctl) + CI_PKG_TO_TEST="cmd/fleetctl/..." +else ifeq ($(CI_TEST_PKG), vuln) + CI_PKG_TO_TEST="server/vulnerabilities/..." +else + CI_PKG_TO_TEST=$(DEFAULT_PKG_TO_TEST) +endif + +ci-pkg-list: + @echo $(CI_PKG_TO_TEST) + .run-go-tests: ifeq ($(PKG_TO_TEST), "") - @echo "Please specify one or more packages to test with argument PKG_TO_TEST=\"/path/to/pkg/1 /path/to/pkg/2\"..."; + @echo "Please specify one or more packages to test with argument PKG_TO_TEST=\"/path/to/pkg/1 /path/to/pkg/2\"..."; else @echo Running Go tests with command: go test -tags full,fts5,netgo -run=${TESTS_TO_RUN} ${GO_TEST_MAKE_FLAGS} ${GO_TEST_EXTRA_FLAGS} -parallel 8 -coverprofile=coverage.txt -covermode=atomic -coverpkg=github.com/fleetdm/fleet/v4/... $(go_test_pkg_to_test) @@ -171,10 +187,10 @@ endif # GO_TEST_EXTRA_FLAGS: Used to specify other arguments to `go test`. .debug-go-tests: ifeq ($(PKG_TO_TEST), "") - @echo "Please specify one or more packages to debug with argument PKG_TO_TEST=\"/path/to/pkg/1 /path/to/pkg/2\"..."; + @echo "Please specify one or more packages to debug with argument PKG_TO_TEST=\"/path/to/pkg/1 /path/to/pkg/2\"..."; else @echo Debugging tests with command: - dlv test ${dlv_test_pkg_to_test} --api-version=2 --listen=127.0.0.1:61179 ${DEBUG_TEST_EXTRA_FLAGS} -- -test.v -test.run=${TESTS_TO_RUN} ${GO_TEST_EXTRA_FLAGS} + dlv test ${dlv_test_pkg_to_test} --api-version=2 --listen=127.0.0.1:61179 ${DEBUG_TEST_EXTRA_FLAGS} -- -test.v -test.run=${TESTS_TO_RUN} ${GO_TEST_EXTRA_FLAGS} endif # Command to run specific tests in development. Can run all tests for one or more packages, or specific tests within packages. @@ -182,11 +198,11 @@ run-go-tests: @MYSQL_TEST=1 REDIS_TEST=1 MINIO_STORAGE_TEST=1 SAML_IDP_TEST=1 NETWORK_TEST=1 make .run-go-tests GO_TEST_MAKE_FLAGS="-v" debug-go-tests: - @MYSQL_TEST=1 REDIS_TEST=1 MINIO_STORAGE_TEST=1 SAML_IDP_TEST=1 NETWORK_TEST=1 make .debug-go-tests + @MYSQL_TEST=1 REDIS_TEST=1 MINIO_STORAGE_TEST=1 SAML_IDP_TEST=1 NETWORK_TEST=1 make .debug-go-tests # Command used in CI to run all tests. -test-go: dump-test-schema generate-mock - make .run-go-tests PKG_TO_TEST="./cmd/... ./ee/... ./orbit/pkg/... ./orbit/cmd/orbit ./pkg/... ./server/... ./tools/..." +test-go: dump-test-schema generate-mock + make .run-go-tests PKG_TO_TEST="$(CI_PKG_TO_TEST)" analyze-go: go test -tags full,fts5,netgo -race -cover ./... diff --git a/articles/roadmap-preview-january-2025.md b/articles/roadmap-preview-january-2025.md new file mode 100644 index 000000000000..11a0f3075008 --- /dev/null +++ b/articles/roadmap-preview-january-2025.md @@ -0,0 +1,32 @@ +# Roadmap preview, January 2025 + +
+ +
+ +The Fleet roadmap is set for the first three months of 2025. Watch the video above for a walkthrough, or continue reading for the highlights. + +In the next 3 months, Fleet will ship... +- πŸ›‘οΈ Integration with Microsoft compliance tools +- 🦾 Read-only GitOps mode +- πŸ’ Fleet-maintained apps for Windows +- β˜‘οΈ Integration with DigiCert +- 🎑 One queue for MDM commands, software, and scripts +- πŸ“± Android MDM for personal devices (BYOD) +- πŸ” More data for detection & response and threat hunting +- πŸ”„ Auto-patch software without writing custom policies +- πŸ§‘β€πŸ’Ό Identify provider host vitals + +Big opportunities that Fleet is building towards in the near future (next 180 days): +- 🍏 Account-based user enrollment for personal devices (BYOD) +- πŸ—“οΈ Native patching for apps and OS during maintenance windows +- πŸ€– AI-generated osquery queries + +Any feedback or a questions? You can find us where we hang out in the [osquery // #fleet Slack channel](https://chat.osquery.io/c/fleet). + + + + + + + diff --git a/articles/teams.md b/articles/teams.md index ea688bc08b0c..934be78511e7 100644 --- a/articles/teams.md +++ b/articles/teams.md @@ -20,7 +20,7 @@ Fleet's best practice teams: - `Compliance exclusions`: All contributors' test work computers or virtual machines (VMs). Used for validating workflows for Fleet customers or reproducing bugs in the Fleet product. - `πŸ“±πŸ’ Company-owned iPhones`: iPhones purchased by the organization that enroll to Fleet automatically via Apple Business Manager. For example, iPhones used by iOS Engineers. - `πŸ”³πŸ’ Company-owned iPads`: iPads purchased by the organization that enroll to Fleet automatically via Apple Business Manager. For example, conference-room iPads. - +- `πŸ“±πŸ” Personally-owned iPhones`: End users' personal iPhones, like those enrolled through a BYOD program, that have access to company resources. If some of your hosts don't fall under the above teams, what are these hosts for? The answer determines the the hosts' risk/compliance needs, and thus their security basline, and thus their "team" in Fleet. If the hosts' have a different compliance needs, and thus different security baseline, then it's time to create a new team in Fleet. diff --git a/changes/23971-persist-hosts-column-settings-across-sessions b/changes/23971-persist-hosts-column-settings-across-sessions new file mode 100644 index 000000000000..294b74aba632 --- /dev/null +++ b/changes/23971-persist-hosts-column-settings-across-sessions @@ -0,0 +1,2 @@ +- Implement user-level settings, use them to persist a user's selection of which columns to display + on the hosts table. diff --git a/changes/24962-ui-dashboard-mdm-solutions-table b/changes/24962-ui-dashboard-mdm-solutions-table new file mode 100644 index 000000000000..972a18bf8087 --- /dev/null +++ b/changes/24962-ui-dashboard-mdm-solutions-table @@ -0,0 +1 @@ +- Removed arrow icon from MDM solution table on dashboard page. diff --git a/changes/25072-25073-software-name-overflow b/changes/25072-25073-software-name-overflow new file mode 100644 index 000000000000..682d0c6e2fe7 --- /dev/null +++ b/changes/25072-25073-software-name-overflow @@ -0,0 +1 @@ +* Fleet UI: Fixed software name overflow in various modals \ No newline at end of file diff --git a/changes/25114-include-team-queries-in-host-details-query-modal b/changes/25114-include-team-queries-in-host-details-query-modal new file mode 100644 index 000000000000..6d151975002d --- /dev/null +++ b/changes/25114-include-team-queries-in-host-details-query-modal @@ -0,0 +1,2 @@ +* Include a host's team-level queries when the user is selecting a query to target for a specific +host via the host details page. diff --git a/changes/25265-boostrap-package-not-found b/changes/25265-boostrap-package-not-found new file mode 100644 index 000000000000..5089e6c29c0e --- /dev/null +++ b/changes/25265-boostrap-package-not-found @@ -0,0 +1 @@ +Downgraded expected/common "BootstrapPackage not found" server error to a debug message. Occurs when UI/API checks if bootstrap package exists. diff --git a/cmd/osquery-perf/agent.go b/cmd/osquery-perf/agent.go index ed1e5404800e..8587ce2b7c55 100644 --- a/cmd/osquery-perf/agent.go +++ b/cmd/osquery-perf/agent.go @@ -18,6 +18,7 @@ import ( "net/http" _ "net/http/pprof" "os" + "regexp" "sort" "strconv" "strings" @@ -1858,10 +1859,89 @@ func (a *agent) runLiveQuery(query string) (results []map[string]string, status ss := fleet.OsqueryStatus(1) return []map[string]string{}, &ss, ptr.String("live query failed with error foobar"), nil } - ss := fleet.OsqueryStatus(0) if a.liveQueryNoResultsProb > 0.0 && rand.Float64() <= a.liveQueryNoResultsProb { + ss := fleet.OsqueryStatus(0) return []map[string]string{}, &ss, nil, nil } + + // Switch based on contents of the query. + lcQuery := strings.ToLower(query) + switch { + case strings.Contains(lcQuery, "from yara") && strings.Contains(lcQuery, "sigurl"): + return a.runLiveYaraQuery(query) + default: + return a.runLiveMockQuery(query) + } +} + +func (a *agent) runLiveYaraQuery(query string) (results []map[string]string, status *fleet.OsqueryStatus, message *string, stats *fleet.Stats) { + // Get the URL of the YARA rule to request (i.e. the sigurl). + urlRegex := regexp.MustCompile(`sigurl=(["'])([^"']*)["']`) + matches := urlRegex.FindStringSubmatch(query) + var url string + if len(matches) > 2 { + url = matches[2] + } else { + ss := fleet.OsqueryStatus(1) + return []map[string]string{}, &ss, ptr.String("live yara query failed because a valid sigurl could not be found"), nil + } + + // Osquery validates that the sigurl is one of a configured set, so that it's not + // sending requests to just anywhere. We'll check that it's at least the same host + // as the Fleet server. + if !strings.HasPrefix(url, a.serverAddress) { + ss := fleet.OsqueryStatus(1) + return []map[string]string{}, &ss, ptr.String("live yara query failed because sigurl host did not match server address"), nil + } + + // Make the request. + body := []byte(`{"node_key": "` + a.nodeKey + `"}`) + request, err := http.NewRequest("POST", url, bytes.NewReader(body)) + if err != nil { + ss := fleet.OsqueryStatus(1) + return []map[string]string{}, &ss, ptr.String("live yara query failed due to error creating request"), nil + } + request.Header.Add("Content-type", "application/json") + + // Make the request. + response, err := http.DefaultClient.Do(request) + if err != nil { + ss := fleet.OsqueryStatus(1) + return []map[string]string{}, &ss, ptr.String(fmt.Sprintf("yara request failed to run: %v", err)), nil + } + defer response.Body.Close() + + // For load testing purposes we don't actually care about the response, but check that we at least got one. + if _, err := io.Copy(io.Discard, response.Body); err != nil { + ss := fleet.OsqueryStatus(1) + return []map[string]string{}, &ss, ptr.String(fmt.Sprintf("error reading response from yara API: %v", err)), nil + } + + // Return a response indicating that the file is clean. + ss := fleet.OsqueryStatus(0) + return []map[string]string{ + { + "count": "0", + "matches": "", + "strings": "", + "tags": "", + "sig_group": "", + "sigfile": "", + "sigrule": "", + "sigurl": url, + // Could pull this from the query, but not necessary for load testing. + "path": "/some/path", + }, + }, &ss, nil, &fleet.Stats{ + WallTimeMs: uint64(rand.Intn(1000) * 1000), + UserTime: uint64(rand.Intn(1000)), + SystemTime: uint64(rand.Intn(1000)), + Memory: uint64(rand.Intn(1000)), + } +} + +func (a *agent) runLiveMockQuery(query string) (results []map[string]string, status *fleet.OsqueryStatus, message *string, stats *fleet.Stats) { + ss := fleet.OsqueryStatus(0) return []map[string]string{ { "admindir": "/var/lib/dpkg", diff --git a/docs/Deploy/Reference-Architectures.md b/docs/Deploy/Reference-Architectures.md index c75e0a7ad3f1..20e1b7702a5e 100644 --- a/docs/Deploy/Reference-Architectures.md +++ b/docs/Deploy/Reference-Architectures.md @@ -111,7 +111,7 @@ In order for osqueryd clients to connect, the connection to Fleet must use TLS. ## Using a proxy -If you are in an enterprise environment where Fleet is behind a proxy and you would like to be able to retrieve vulnerability data for [vulnerability processing](https://fleetdm.com/docs/using-fleet/vulnerability-processing#vulnerability-processing), it may be necessary to configure the proxy settings. Fleet automatically uses the `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` environment variables. +In enterprise environments where Fleet operates behind a proxy, you may need to configure proxy settings to enable services requiring outbound traffic, such as [vulnerability processing](https://fleetdm.com/docs/using-fleet/vulnerability-processing#vulnerability-processing) or [device management](https://fleetdm.com/device-management). Fleet automatically uses the `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` environment variables. For example, to configure the proxy in a systemd service file: @@ -179,7 +179,7 @@ assume On-Demand pricing (savings are available through Reserved Instances). Cal | Dependencies | Version | Instance type | Nodes | | ------------ | ----------------------- | --------------- | ----- | | Redis | 6 | cache.t4g.small | 3 | -| MySQL | 8.0.mysql_aurora.3.04.2 | db.t4g.medium | 2 | +| MySQL | 8.0.mysql_aurora.3.07.1 | db.t4g.medium | 2 | ###### [Up to 25000 hosts](https://calculator.aws/#/estimate?id=d735758715f059118dbce8dc42f3ff2410adc621) @@ -191,7 +191,7 @@ assume On-Demand pricing (savings are available through Reserved Instances). Cal | Dependencies | Version | Instance type | Nodes | | ------------ | ----------------------- | --------------- | ----- | | Redis | 6 | cache.m6g.large | 3 | -| MySQL | 8.0.mysql_aurora.3.04.2 | db.r6g.large | 2 | +| MySQL | 8.0.mysql_aurora.3.07.1 | db.r6g.large | 2 | ###### [Up to 150000 hosts](https://calculator.aws/#/estimate?id=689fea65efff361ee070b15044a01224b8d26621) @@ -203,7 +203,7 @@ assume On-Demand pricing (savings are available through Reserved Instances). Cal | Dependencies | Version | Instance type | Nodes | | ------------ | ----------------------- | --------------- | ----- | | Redis | 6 | cache.m6g.large | 3 | -| MySQL | 8.0.mysql_aurora.3.04.2 | db.r6g.4xlarge | 2 | +| MySQL | 8.0.mysql_aurora.3.07.1 | db.r6g.4xlarge | 2 | ###### [Up to 300000 hosts](https://calculator.aws/#/estimate?id=19b667fde567df0d64d9fae632d4885d7fdc726a) @@ -215,7 +215,7 @@ assume On-Demand pricing (savings are available through Reserved Instances). Cal | Dependencies | Version | Instance type | Nodes | | ------------ | ----------------------- | --------------- | ----- | | Redis | 6 | cache.m6g.large | 3 | -| MySQL | 8.0.mysql_aurora.3.04.2 | db.r6g.16xlarge | 2 | +| MySQL | 8.0.mysql_aurora.3.07.1 | db.r6g.16xlarge | 2 | AWS reference architecture can be found [here](https://github.com/fleetdm/fleet/tree/main/terraform/example). This configuration includes: diff --git a/docs/Get started/FAQ.md b/docs/Get started/FAQ.md index cb63e6448381..2bd9b4d30c8a 100644 --- a/docs/Get started/FAQ.md +++ b/docs/Get started/FAQ.md @@ -106,26 +106,6 @@ The `fleetctl package` command is not supported on DISA-STIG distribution. Different portions of the Fleet software are licensed differently, as noted in the [LICENSE](https://github.com/fleetdm/fleet/blob/main/LICENSE) file. The majority of Fleet is MIT licensed. Paid features require a license key. -## What is your commitment to open source stewardship? - -- When a feature is free and open source we won't move that feature to a paid tier. Features might be removed from the open source codebase in other cases, for example when combining features from multiple tiers into one new feature. - -- The majority of new capabilities added to Fleet will benefit all users, not just customers. - -- We won't introduce features into the open source codebase with a fixed delay; if a feature is planned to land in both it will be released simultaneously in both. - -- We will always release and open source all tests that we have for any open source feature. - -- The free version of Fleet is enterprise ready. - -- The open source codebase will not contain any artificial limits on the number of hosts, users, size, or performance. - -- The majority of new features contributed by Fleet Device Management Inc will be open source. - -- The product will be available for download without leaving an email address or logging in. - -- We will always allow you to benchmark the performance of Fleet. (Fleet also [load tests the platform before every release](https://fleetdm.com/handbook/engineering#rituals), with increasingly ambitious targets. The scale of real time reporting supported by Fleet has increased 5,000% since 2019. Today, Fleet deployments support 500,000 devices, and counting. The company is committed to driving this number to 1M+, and beyond.) - ## How do I contact Fleet for support? A lot of questions can be answered [in the documentation](https://fleetdm.com/docs) or [guides](https://fleetdm.com/guides). @@ -134,22 +114,10 @@ To get help from the community, visit https://fleetdm.com/support. If your organization has Fleet Premium, you can [access professional support](https://fleetdm.com/customers/login) with a guaranteed response time. -## What if we choose not to renew? - -If you opt not to renew Fleet Premium, you can continue using only the free capabilities of Fleet (same code base, just unconfigure the license key.) - -## Can we buy a license to access premium features with reduced support for a reduced cost? - -We aren’t able to sell licenses and support separately. - ## Do you offer pricing for unmanaged hosts? What about ephemeral hosts which may scale up or down? For now, the number of hosts is the maximum cap of hosts enrolled at any given time. Umanaged hosts ("Pending" MDM status in Fleet) are not included in the enrolled hosts count. -## When run locally, what resources does the Fleet app typically consume on an individual instance, and when run in HA, at high volume? And how is latency on an individual instance vs clustered deployment? - -Like any modern application, Fleet scales horizontally. The biggest potential bottleneck for Fleet is the number of hosts being monitored, so that's where we've devoted the most attention when testing. The largest number of hosts we've had a customer ask about was 350,000, for all of the production servers and employee laptops of a publicly traded company. - ## Where's the data stored? Since Fleet is self-managed, some metadata is stored wherever it is deployed (e.g. Amazon, Azure, Google, your own data center, hybrid cloud, anywhere). That's done using a MySQL database, but the bulk of the data flows directly into a tool like Splunk or ElasticSearch. You can send that information to any of Fleet's supported log destinations. @@ -168,6 +136,26 @@ The only way we are able to partner as a business to provide support and build n ## How can I uninstall fleetd? To uninstall Fleet's agent (fleetd), follow the instructions [here](https://fleetdm.com/guides/how-to-uninstall-fleetd). +## What is your commitment to open source stewardship? + +- When a feature is free and open source we won't move that feature to a paid tier. Features might be removed from the open source codebase in other cases, for example when combining features from multiple tiers into one new feature. + +- The majority of new capabilities added to Fleet will benefit all users, not just customers. + +- We won't introduce features into the open source codebase with a fixed delay; if a feature is planned to land in both it will be released simultaneously in both. + +- We will always release and open source all tests that we have for any open source feature. + +- The free version of Fleet is enterprise ready. + +- The open source codebase will not contain any artificial limits on the number of hosts, users, size, or performance. + +- The majority of new features contributed by Fleet Device Management Inc will be open source. + +- The product will be available for download without leaving an email address or logging in. + +- We will always allow you to benchmark the performance of Fleet. (Fleet also [load tests the platform before every release](https://fleetdm.com/handbook/engineering#rituals), with increasingly ambitious targets. The scale of real time reporting supported by Fleet has increased 5,000% since 2019. Today, Fleet deployments support 500,000 devices, and counting. The company is committed to driving this number to 1M+, and beyond.) + diff --git a/handbook/company/open-positions.yml b/handbook/company/open-positions.yml index e36e4740ac3c..90ccd33d8d5c 100644 --- a/handbook/company/open-positions.yml +++ b/handbook/company/open-positions.yml @@ -59,3 +59,29 @@ - πŸ’– An excellent understanding of macOS, Windows, Linux and core services like Autopilot, ABM/ASM, MDM, ADE, APNs, syslog, etc. - ✍️ Familiarity with SQLite, shell scripting, Python, Powershell, and using the terminal to execute commands or run scripts is a bonus. - πŸ§‘β€πŸ”¬ Experience working with enterprise customers to help resolve complex technical issues. + +- jobTitle: 🫧 Account Development Representative + department: Demand + hiringManagerName: Drew Baker + hiringManagerGithubUsername: drew-p-drawers + hiringManagerLinkedInUrl: https://www.linkedin.com/in/andrew-baker-51547179/ + responsibilities: | + - πŸ”§ Manually research, collect, and organize CRM data. + - πŸ“ˆ Track and analyze sales performance using CRM software and other tools + - 🀝 Work with internal teams to develop and maintain external relationships. + - πŸ§‘β€πŸ’Ό Represent Fleet and interact with the community at events and meetups as well as using multiple social media platforms. + - πŸ—£οΈ Identify and pursue new business opportunities through research and networking. + - 🦺 Help manage the flow of "planned" and "unplanned" work using multiple tools and ticketing systems. + - πŸ“£ Collaborate with internal teams (sales, marketing, product) to ensure alignment and effective communication. + - πŸ§‘β€πŸ’» Work remotely, both within a team and individually, to help develop, document, and perform relevant responsibilities outlined at https://fleetdm.com/handbook/demand#responsibilities. + experience: | + - πŸƒβ€β™‚οΈ Strong desire to build a technical and operational-based skill set. + - πŸš€ Detail-oriented, highly organized, and able to move quickly to solve complex problems using boring solutions. + - πŸ¦‰ Good understanding of Google Suite (Gmail, Google Calendar, Google Sheets, Google Docs, etc.) + - πŸ«€ Experience dealing with sensitive personal information of team members and customers. + - ✍️ Strong writing and oral communication for general and technical topics. + - πŸ’­ Capable of understanding and translating technical concepts and personas. + - πŸ› οΈ Ability to work in a process-driven team-based environment. + - 🟣 Openness: You are flexible and open to new ideas and ways of working. + - βž• Bonus: Customer service\support background. + diff --git a/handbook/company/why-this-way.md b/handbook/company/why-this-way.md index c520a7dfa6c6..e3ed816d86a1 100644 --- a/handbook/company/why-this-way.md +++ b/handbook/company/why-this-way.md @@ -313,7 +313,7 @@ In sentence case, we write and capitalize words as if they were in sentences: > Ask questions about your servers, containers, and laptops running Linux, Windows, and macOS -As we use sentence case, only the first word is capitalized. But, if a word would normally be capitalized in the sentence (e.g., a proper noun, an acronym, or a stylization) it should remain capitalized. User roles (e.g., "observer" or "maintainer") and features (e.g. "automations") in the Fleet product aren't treated as proper nouns and shouldn't be capitalized. +As we use sentence case, only the first word is capitalized. But, if a word would normally be capitalized in the sentence (e.g., a proper noun, an acronym, or a stylization) it should remain capitalized. User roles (e.g., "observer" or "maintainer") and features (e.g. "automations") in the Fleet product aren't treated as proper nouns and shouldn't be capitalized. Words/phrases that follow steps numbers (e.g. "Step 1: create") in the documentation shouldn't be capitalized. The reason for sentence case at Fleet is that everyone capitalizes differently in English, and capitalization conventions have not been taught very consistently in schools. Sentence case simplifies capitalization rules so that contributors can deliver more natural, even-looking content with a voice that feels similar no matter where you're reading it. diff --git a/handbook/digital-experience/digital-experience.rituals.yml b/handbook/digital-experience/digital-experience.rituals.yml index 9e33fe31b965..abb42ab67d32 100644 --- a/handbook/digital-experience/digital-experience.rituals.yml +++ b/handbook/digital-experience/digital-experience.rituals.yml @@ -87,9 +87,6 @@ description: "Using your departmental kanban board, prioritize and finalize next sprint's goals for your team by draging the appropriate issues to the top of the 'Not yet' column." # example of a longer thing: description: "[Prioritizing next sprint](https://fleetdm.com/handbook/digital-experiencemunication)" moreInfoUrl: "https://fleetdm.com/handbook/company/why-this-way#why-make-work-visible" #URL used to highlight "description:" test in table dri: "sampfluger88" # DRI for ritual (assignee if autoIssue) (TODO display GitHub proflie pic instead of name or title) - autoIssue: # Enables automation of GitHub issues - labels: [ "#g-digital-experience" ] # label to be applied to issue - repo: "confidential" # The GitHub repo that issues will be created in - task: "Process the CEO's inbox" startedOn: "2023-07-29" @@ -118,9 +115,6 @@ description: "Process and backup E-group agenda" moreInfoUrl: "https://fleetdm.com/handbook/digital-experience#process-and-backup-sid-agenda" dri: "SFriendLee" - autoIssue: - labels: [ "#g-digital-experience" ] - repo: "confidential" - task: "Process and backup Sid agenda" startedOn: "2023-09-25" @@ -128,9 +122,6 @@ description: "Process and backup Sid agenda" moreInfoUrl: "https://fleetdm.com/handbook/digital-experience#process-and-backup-e-group-agenda" dri: "SFriendLee" - autoIssue: - labels: [ "#g-digital-experience" ] - repo: "confidential" - task: "Share recording of all hands meeting" startedOn: "2023-07-01" diff --git a/orbit/changes/8986-systemdrive-env-passthrough b/orbit/changes/8986-systemdrive-env-passthrough deleted file mode 100644 index 472460428128..000000000000 --- a/orbit/changes/8986-systemdrive-env-passthrough +++ /dev/null @@ -1 +0,0 @@ -- Pass `SystemDrive` Environemt variable to osqueryd when present in Orbit diff --git a/orbit/cmd/orbit/orbit.go b/orbit/cmd/orbit/orbit.go index c7d41f11acb7..2cf8138045fd 100644 --- a/orbit/cmd/orbit/orbit.go +++ b/orbit/cmd/orbit/orbit.go @@ -771,12 +771,6 @@ func main() { ) } - if runtime.GOOS == "windows" { - if systemDrive, ok := os.LookupEnv("SystemDrive"); ok { - options = append(options, osquery.WithEnv([]string{fmt.Sprintf("SystemDrive=%s", systemDrive)})) - } - } - var certPath string if fleetURL != "https://" && c.Bool("insecure") { proxy, err := insecure.NewTLSProxy(fleetURL) diff --git a/server/contexts/logging/logging.go b/server/contexts/logging/logging.go index 8ef8236a272e..056f6a81ad87 100644 --- a/server/contexts/logging/logging.go +++ b/server/contexts/logging/logging.go @@ -53,6 +53,14 @@ func WithNoUser(ctx context.Context) context.Context { return ctx } +// WithNoError returns a context with logging.SkipError set to true so error won't be logged at level=error +func WithNoError(ctx context.Context) context.Context { + if logCtx, ok := FromContext(ctx); ok { + logCtx.SetSkipError() + } + return ctx +} + // WithExtras returns a context with logging.Extras set as the values provided func WithExtras(ctx context.Context, extras ...interface{}) context.Context { if logCtx, ok := FromContext(ctx); ok { @@ -61,6 +69,8 @@ func WithExtras(ctx context.Context, extras ...interface{}) context.Context { return ctx } +// WithLevel forces a log level for the current request/context. +// Level may still be upgraded to Error if an error is present. func WithLevel(ctx context.Context, level func(kitlog.Logger) kitlog.Logger) context.Context { if logCtx, ok := FromContext(ctx); ok { logCtx.SetForceLevel(level) @@ -77,6 +87,7 @@ type LoggingContext struct { Extras []interface{} SkipUser bool ForceLevel func(kitlog.Logger) kitlog.Logger + SkipError bool } func (l *LoggingContext) SetForceLevel(level func(kitlog.Logger) kitlog.Logger) { @@ -97,6 +108,12 @@ func (l *LoggingContext) SetSkipUser() { l.SkipUser = true } +func (l *LoggingContext) SetSkipError() { + l.l.Lock() + defer l.l.Unlock() + l.SkipError = true +} + func (l *LoggingContext) SetStartTime() { l.l.Lock() defer l.l.Unlock() @@ -115,7 +132,7 @@ func (l *LoggingContext) Log(ctx context.Context, logger kitlog.Logger) { defer l.l.Unlock() switch { - case len(l.Errs) > 0: + case len(l.Errs) > 0 && !l.SkipError: logger = level.Error(logger) case l.ForceLevel != nil: logger = l.ForceLevel(logger) diff --git a/server/datastore/mysql/migrations/tables/20250107165731_AddSettingsColumnToUsersTable.go b/server/datastore/mysql/migrations/tables/20250107165731_AddSettingsColumnToUsersTable.go new file mode 100644 index 000000000000..a43c377d753c --- /dev/null +++ b/server/datastore/mysql/migrations/tables/20250107165731_AddSettingsColumnToUsersTable.go @@ -0,0 +1,22 @@ +package tables + +import ( + "database/sql" + "fmt" +) + +func init() { + MigrationClient.AddMigration(Up_20250107165731, Down_20250107165731) +} + +func Up_20250107165731(tx *sql.Tx) error { + _, err := tx.Exec(`ALTER TABLE users ADD COLUMN settings json NOT NULL DEFAULT (JSON_OBJECT())`) + if err != nil { + return fmt.Errorf("failed to add settings to users: %w", err) + } + return nil +} + +func Down_20250107165731(tx *sql.Tx) error { + return nil +} diff --git a/server/datastore/mysql/schema.sql b/server/datastore/mysql/schema.sql index 4588000e1f9d..50b32b038c83 100644 --- a/server/datastore/mysql/schema.sql +++ b/server/datastore/mysql/schema.sql @@ -1111,9 +1111,9 @@ CREATE TABLE `migration_status_tables` ( `is_applied` tinyint(1) NOT NULL, `tstamp` timestamp NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) -) /*!50100 TABLESPACE `innodb_system` */ ENGINE=InnoDB AUTO_INCREMENT=346 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +) /*!50100 TABLESPACE `innodb_system` */ ENGINE=InnoDB AUTO_INCREMENT=347 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -INSERT INTO `migration_status_tables` VALUES (1,0,1,'2020-01-01 01:01:01'),(2,20161118193812,1,'2020-01-01 01:01:01'),(3,20161118211713,1,'2020-01-01 01:01:01'),(4,20161118212436,1,'2020-01-01 01:01:01'),(5,20161118212515,1,'2020-01-01 01:01:01'),(6,20161118212528,1,'2020-01-01 01:01:01'),(7,20161118212538,1,'2020-01-01 01:01:01'),(8,20161118212549,1,'2020-01-01 01:01:01'),(9,20161118212557,1,'2020-01-01 01:01:01'),(10,20161118212604,1,'2020-01-01 01:01:01'),(11,20161118212613,1,'2020-01-01 01:01:01'),(12,20161118212621,1,'2020-01-01 01:01:01'),(13,20161118212630,1,'2020-01-01 01:01:01'),(14,20161118212641,1,'2020-01-01 01:01:01'),(15,20161118212649,1,'2020-01-01 01:01:01'),(16,20161118212656,1,'2020-01-01 01:01:01'),(17,20161118212758,1,'2020-01-01 01:01:01'),(18,20161128234849,1,'2020-01-01 01:01:01'),(19,20161230162221,1,'2020-01-01 01:01:01'),(20,20170104113816,1,'2020-01-01 01:01:01'),(21,20170105151732,1,'2020-01-01 01:01:01'),(22,20170108191242,1,'2020-01-01 01:01:01'),(23,20170109094020,1,'2020-01-01 01:01:01'),(24,20170109130438,1,'2020-01-01 01:01:01'),(25,20170110202752,1,'2020-01-01 01:01:01'),(26,20170111133013,1,'2020-01-01 01:01:01'),(27,20170117025759,1,'2020-01-01 01:01:01'),(28,20170118191001,1,'2020-01-01 01:01:01'),(29,20170119234632,1,'2020-01-01 01:01:01'),(30,20170124230432,1,'2020-01-01 01:01:01'),(31,20170127014618,1,'2020-01-01 01:01:01'),(32,20170131232841,1,'2020-01-01 01:01:01'),(33,20170223094154,1,'2020-01-01 01:01:01'),(34,20170306075207,1,'2020-01-01 01:01:01'),(35,20170309100733,1,'2020-01-01 01:01:01'),(36,20170331111922,1,'2020-01-01 01:01:01'),(37,20170502143928,1,'2020-01-01 01:01:01'),(38,20170504130602,1,'2020-01-01 01:01:01'),(39,20170509132100,1,'2020-01-01 01:01:01'),(40,20170519105647,1,'2020-01-01 01:01:01'),(41,20170519105648,1,'2020-01-01 01:01:01'),(42,20170831234300,1,'2020-01-01 01:01:01'),(43,20170831234301,1,'2020-01-01 01:01:01'),(44,20170831234303,1,'2020-01-01 01:01:01'),(45,20171116163618,1,'2020-01-01 01:01:01'),(46,20171219164727,1,'2020-01-01 01:01:01'),(47,20180620164811,1,'2020-01-01 01:01:01'),(48,20180620175054,1,'2020-01-01 01:01:01'),(49,20180620175055,1,'2020-01-01 01:01:01'),(50,20191010101639,1,'2020-01-01 01:01:01'),(51,20191010155147,1,'2020-01-01 01:01:01'),(52,20191220130734,1,'2020-01-01 01:01:01'),(53,20200311140000,1,'2020-01-01 01:01:01'),(54,20200405120000,1,'2020-01-01 01:01:01'),(55,20200407120000,1,'2020-01-01 01:01:01'),(56,20200420120000,1,'2020-01-01 01:01:01'),(57,20200504120000,1,'2020-01-01 01:01:01'),(58,20200512120000,1,'2020-01-01 01:01:01'),(59,20200707120000,1,'2020-01-01 01:01:01'),(60,20201011162341,1,'2020-01-01 01:01:01'),(61,20201021104586,1,'2020-01-01 01:01:01'),(62,20201102112520,1,'2020-01-01 01:01:01'),(63,20201208121729,1,'2020-01-01 01:01:01'),(64,20201215091637,1,'2020-01-01 01:01:01'),(65,20210119174155,1,'2020-01-01 01:01:01'),(66,20210326182902,1,'2020-01-01 01:01:01'),(67,20210421112652,1,'2020-01-01 01:01:01'),(68,20210506095025,1,'2020-01-01 01:01:01'),(69,20210513115729,1,'2020-01-01 01:01:01'),(70,20210526113559,1,'2020-01-01 01:01:01'),(71,20210601000001,1,'2020-01-01 01:01:01'),(72,20210601000002,1,'2020-01-01 01:01:01'),(73,20210601000003,1,'2020-01-01 01:01:01'),(74,20210601000004,1,'2020-01-01 01:01:01'),(75,20210601000005,1,'2020-01-01 01:01:01'),(76,20210601000006,1,'2020-01-01 01:01:01'),(77,20210601000007,1,'2020-01-01 01:01:01'),(78,20210601000008,1,'2020-01-01 01:01:01'),(79,20210606151329,1,'2020-01-01 01:01:01'),(80,20210616163757,1,'2020-01-01 01:01:01'),(81,20210617174723,1,'2020-01-01 01:01:01'),(82,20210622160235,1,'2020-01-01 01:01:01'),(83,20210623100031,1,'2020-01-01 01:01:01'),(84,20210623133615,1,'2020-01-01 01:01:01'),(85,20210708143152,1,'2020-01-01 01:01:01'),(86,20210709124443,1,'2020-01-01 01:01:01'),(87,20210712155608,1,'2020-01-01 01:01:01'),(88,20210714102108,1,'2020-01-01 01:01:01'),(89,20210719153709,1,'2020-01-01 01:01:01'),(90,20210721171531,1,'2020-01-01 01:01:01'),(91,20210723135713,1,'2020-01-01 01:01:01'),(92,20210802135933,1,'2020-01-01 01:01:01'),(93,20210806112844,1,'2020-01-01 01:01:01'),(94,20210810095603,1,'2020-01-01 01:01:01'),(95,20210811150223,1,'2020-01-01 01:01:01'),(96,20210818151827,1,'2020-01-01 01:01:01'),(97,20210818151828,1,'2020-01-01 01:01:01'),(98,20210818182258,1,'2020-01-01 01:01:01'),(99,20210819131107,1,'2020-01-01 01:01:01'),(100,20210819143446,1,'2020-01-01 01:01:01'),(101,20210903132338,1,'2020-01-01 01:01:01'),(102,20210915144307,1,'2020-01-01 01:01:01'),(103,20210920155130,1,'2020-01-01 01:01:01'),(104,20210927143115,1,'2020-01-01 01:01:01'),(105,20210927143116,1,'2020-01-01 01:01:01'),(106,20211013133706,1,'2020-01-01 01:01:01'),(107,20211013133707,1,'2020-01-01 01:01:01'),(108,20211102135149,1,'2020-01-01 01:01:01'),(109,20211109121546,1,'2020-01-01 01:01:01'),(110,20211110163320,1,'2020-01-01 01:01:01'),(111,20211116184029,1,'2020-01-01 01:01:01'),(112,20211116184030,1,'2020-01-01 01:01:01'),(113,20211202092042,1,'2020-01-01 01:01:01'),(114,20211202181033,1,'2020-01-01 01:01:01'),(115,20211207161856,1,'2020-01-01 01:01:01'),(116,20211216131203,1,'2020-01-01 01:01:01'),(117,20211221110132,1,'2020-01-01 01:01:01'),(118,20220107155700,1,'2020-01-01 01:01:01'),(119,20220125105650,1,'2020-01-01 01:01:01'),(120,20220201084510,1,'2020-01-01 01:01:01'),(121,20220208144830,1,'2020-01-01 01:01:01'),(122,20220208144831,1,'2020-01-01 01:01:01'),(123,20220215152203,1,'2020-01-01 01:01:01'),(124,20220223113157,1,'2020-01-01 01:01:01'),(125,20220307104655,1,'2020-01-01 01:01:01'),(126,20220309133956,1,'2020-01-01 01:01:01'),(127,20220316155700,1,'2020-01-01 01:01:01'),(128,20220323152301,1,'2020-01-01 01:01:01'),(129,20220330100659,1,'2020-01-01 01:01:01'),(130,20220404091216,1,'2020-01-01 01:01:01'),(131,20220419140750,1,'2020-01-01 01:01:01'),(132,20220428140039,1,'2020-01-01 01:01:01'),(133,20220503134048,1,'2020-01-01 01:01:01'),(134,20220524102918,1,'2020-01-01 01:01:01'),(135,20220526123327,1,'2020-01-01 01:01:01'),(136,20220526123328,1,'2020-01-01 01:01:01'),(137,20220526123329,1,'2020-01-01 01:01:01'),(138,20220608113128,1,'2020-01-01 01:01:01'),(139,20220627104817,1,'2020-01-01 01:01:01'),(140,20220704101843,1,'2020-01-01 01:01:01'),(141,20220708095046,1,'2020-01-01 01:01:01'),(142,20220713091130,1,'2020-01-01 01:01:01'),(143,20220802135510,1,'2020-01-01 01:01:01'),(144,20220818101352,1,'2020-01-01 01:01:01'),(145,20220822161445,1,'2020-01-01 01:01:01'),(146,20220831100036,1,'2020-01-01 01:01:01'),(147,20220831100151,1,'2020-01-01 01:01:01'),(148,20220908181826,1,'2020-01-01 01:01:01'),(149,20220914154915,1,'2020-01-01 01:01:01'),(150,20220915165115,1,'2020-01-01 01:01:01'),(151,20220915165116,1,'2020-01-01 01:01:01'),(152,20220928100158,1,'2020-01-01 01:01:01'),(153,20221014084130,1,'2020-01-01 01:01:01'),(154,20221027085019,1,'2020-01-01 01:01:01'),(155,20221101103952,1,'2020-01-01 01:01:01'),(156,20221104144401,1,'2020-01-01 01:01:01'),(157,20221109100749,1,'2020-01-01 01:01:01'),(158,20221115104546,1,'2020-01-01 01:01:01'),(159,20221130114928,1,'2020-01-01 01:01:01'),(160,20221205112142,1,'2020-01-01 01:01:01'),(161,20221216115820,1,'2020-01-01 01:01:01'),(162,20221220195934,1,'2020-01-01 01:01:01'),(163,20221220195935,1,'2020-01-01 01:01:01'),(164,20221223174807,1,'2020-01-01 01:01:01'),(165,20221227163855,1,'2020-01-01 01:01:01'),(166,20221227163856,1,'2020-01-01 01:01:01'),(167,20230202224725,1,'2020-01-01 01:01:01'),(168,20230206163608,1,'2020-01-01 01:01:01'),(169,20230214131519,1,'2020-01-01 01:01:01'),(170,20230303135738,1,'2020-01-01 01:01:01'),(171,20230313135301,1,'2020-01-01 01:01:01'),(172,20230313141819,1,'2020-01-01 01:01:01'),(173,20230315104937,1,'2020-01-01 01:01:01'),(174,20230317173844,1,'2020-01-01 01:01:01'),(175,20230320133602,1,'2020-01-01 01:01:01'),(176,20230330100011,1,'2020-01-01 01:01:01'),(177,20230330134823,1,'2020-01-01 01:01:01'),(178,20230405232025,1,'2020-01-01 01:01:01'),(179,20230408084104,1,'2020-01-01 01:01:01'),(180,20230411102858,1,'2020-01-01 01:01:01'),(181,20230421155932,1,'2020-01-01 01:01:01'),(182,20230425082126,1,'2020-01-01 01:01:01'),(183,20230425105727,1,'2020-01-01 01:01:01'),(184,20230501154913,1,'2020-01-01 01:01:01'),(185,20230503101418,1,'2020-01-01 01:01:01'),(186,20230515144206,1,'2020-01-01 01:01:01'),(187,20230517140952,1,'2020-01-01 01:01:01'),(188,20230517152807,1,'2020-01-01 01:01:01'),(189,20230518114155,1,'2020-01-01 01:01:01'),(190,20230520153236,1,'2020-01-01 01:01:01'),(191,20230525151159,1,'2020-01-01 01:01:01'),(192,20230530122103,1,'2020-01-01 01:01:01'),(193,20230602111827,1,'2020-01-01 01:01:01'),(194,20230608103123,1,'2020-01-01 01:01:01'),(195,20230629140529,1,'2020-01-01 01:01:01'),(196,20230629140530,1,'2020-01-01 01:01:01'),(197,20230711144622,1,'2020-01-01 01:01:01'),(198,20230721135421,1,'2020-01-01 01:01:01'),(199,20230721161508,1,'2020-01-01 01:01:01'),(200,20230726115701,1,'2020-01-01 01:01:01'),(201,20230807100822,1,'2020-01-01 01:01:01'),(202,20230814150442,1,'2020-01-01 01:01:01'),(203,20230823122728,1,'2020-01-01 01:01:01'),(204,20230906152143,1,'2020-01-01 01:01:01'),(205,20230911163618,1,'2020-01-01 01:01:01'),(206,20230912101759,1,'2020-01-01 01:01:01'),(207,20230915101341,1,'2020-01-01 01:01:01'),(208,20230918132351,1,'2020-01-01 01:01:01'),(209,20231004144339,1,'2020-01-01 01:01:01'),(210,20231009094541,1,'2020-01-01 01:01:01'),(211,20231009094542,1,'2020-01-01 01:01:01'),(212,20231009094543,1,'2020-01-01 01:01:01'),(213,20231009094544,1,'2020-01-01 01:01:01'),(214,20231016091915,1,'2020-01-01 01:01:01'),(215,20231024174135,1,'2020-01-01 01:01:01'),(216,20231025120016,1,'2020-01-01 01:01:01'),(217,20231025160156,1,'2020-01-01 01:01:01'),(218,20231031165350,1,'2020-01-01 01:01:01'),(219,20231106144110,1,'2020-01-01 01:01:01'),(220,20231107130934,1,'2020-01-01 01:01:01'),(221,20231109115838,1,'2020-01-01 01:01:01'),(222,20231121054530,1,'2020-01-01 01:01:01'),(223,20231122101320,1,'2020-01-01 01:01:01'),(224,20231130132828,1,'2020-01-01 01:01:01'),(225,20231130132931,1,'2020-01-01 01:01:01'),(226,20231204155427,1,'2020-01-01 01:01:01'),(227,20231206142340,1,'2020-01-01 01:01:01'),(228,20231207102320,1,'2020-01-01 01:01:01'),(229,20231207102321,1,'2020-01-01 01:01:01'),(230,20231207133731,1,'2020-01-01 01:01:01'),(231,20231212094238,1,'2020-01-01 01:01:01'),(232,20231212095734,1,'2020-01-01 01:01:01'),(233,20231212161121,1,'2020-01-01 01:01:01'),(234,20231215122713,1,'2020-01-01 01:01:01'),(235,20231219143041,1,'2020-01-01 01:01:01'),(236,20231224070653,1,'2020-01-01 01:01:01'),(237,20240110134315,1,'2020-01-01 01:01:01'),(238,20240119091637,1,'2020-01-01 01:01:01'),(239,20240126020642,1,'2020-01-01 01:01:01'),(240,20240126020643,1,'2020-01-01 01:01:01'),(241,20240129162819,1,'2020-01-01 01:01:01'),(242,20240130115133,1,'2020-01-01 01:01:01'),(243,20240131083822,1,'2020-01-01 01:01:01'),(244,20240205095928,1,'2020-01-01 01:01:01'),(245,20240205121956,1,'2020-01-01 01:01:01'),(246,20240209110212,1,'2020-01-01 01:01:01'),(247,20240212111533,1,'2020-01-01 01:01:01'),(248,20240221112844,1,'2020-01-01 01:01:01'),(249,20240222073518,1,'2020-01-01 01:01:01'),(250,20240222135115,1,'2020-01-01 01:01:01'),(251,20240226082255,1,'2020-01-01 01:01:01'),(252,20240228082706,1,'2020-01-01 01:01:01'),(253,20240301173035,1,'2020-01-01 01:01:01'),(254,20240302111134,1,'2020-01-01 01:01:01'),(255,20240312103753,1,'2020-01-01 01:01:01'),(256,20240313143416,1,'2020-01-01 01:01:01'),(257,20240314085226,1,'2020-01-01 01:01:01'),(258,20240314151747,1,'2020-01-01 01:01:01'),(259,20240320145650,1,'2020-01-01 01:01:01'),(260,20240327115530,1,'2020-01-01 01:01:01'),(261,20240327115617,1,'2020-01-01 01:01:01'),(262,20240408085837,1,'2020-01-01 01:01:01'),(263,20240415104633,1,'2020-01-01 01:01:01'),(264,20240430111727,1,'2020-01-01 01:01:01'),(265,20240515200020,1,'2020-01-01 01:01:01'),(266,20240521143023,1,'2020-01-01 01:01:01'),(267,20240521143024,1,'2020-01-01 01:01:01'),(268,20240601174138,1,'2020-01-01 01:01:01'),(269,20240607133721,1,'2020-01-01 01:01:01'),(270,20240612150059,1,'2020-01-01 01:01:01'),(271,20240613162201,1,'2020-01-01 01:01:01'),(272,20240613172616,1,'2020-01-01 01:01:01'),(273,20240618142419,1,'2020-01-01 01:01:01'),(274,20240625093543,1,'2020-01-01 01:01:01'),(275,20240626195531,1,'2020-01-01 01:01:01'),(276,20240702123921,1,'2020-01-01 01:01:01'),(277,20240703154849,1,'2020-01-01 01:01:01'),(278,20240707134035,1,'2020-01-01 01:01:01'),(279,20240707134036,1,'2020-01-01 01:01:01'),(280,20240709124958,1,'2020-01-01 01:01:01'),(281,20240709132642,1,'2020-01-01 01:01:01'),(282,20240709183940,1,'2020-01-01 01:01:01'),(283,20240710155623,1,'2020-01-01 01:01:01'),(284,20240723102712,1,'2020-01-01 01:01:01'),(285,20240725152735,1,'2020-01-01 01:01:01'),(286,20240725182118,1,'2020-01-01 01:01:01'),(287,20240726100517,1,'2020-01-01 01:01:01'),(288,20240730171504,1,'2020-01-01 01:01:01'),(289,20240730174056,1,'2020-01-01 01:01:01'),(290,20240730215453,1,'2020-01-01 01:01:01'),(291,20240730374423,1,'2020-01-01 01:01:01'),(292,20240801115359,1,'2020-01-01 01:01:01'),(293,20240802101043,1,'2020-01-01 01:01:01'),(294,20240802113716,1,'2020-01-01 01:01:01'),(295,20240814135330,1,'2020-01-01 01:01:01'),(296,20240815000000,1,'2020-01-01 01:01:01'),(297,20240815000001,1,'2020-01-01 01:01:01'),(298,20240816103247,1,'2020-01-01 01:01:01'),(299,20240820091218,1,'2020-01-01 01:01:01'),(300,20240826111228,1,'2020-01-01 01:01:01'),(301,20240826160025,1,'2020-01-01 01:01:01'),(302,20240829165448,1,'2020-01-01 01:01:01'),(303,20240829165605,1,'2020-01-01 01:01:01'),(304,20240829165715,1,'2020-01-01 01:01:01'),(305,20240829165930,1,'2020-01-01 01:01:01'),(306,20240829170023,1,'2020-01-01 01:01:01'),(307,20240829170033,1,'2020-01-01 01:01:01'),(308,20240829170044,1,'2020-01-01 01:01:01'),(309,20240905105135,1,'2020-01-01 01:01:01'),(310,20240905140514,1,'2020-01-01 01:01:01'),(311,20240905200000,1,'2020-01-01 01:01:01'),(312,20240905200001,1,'2020-01-01 01:01:01'),(313,20241002104104,1,'2020-01-01 01:01:01'),(314,20241002104105,1,'2020-01-01 01:01:01'),(315,20241002104106,1,'2020-01-01 01:01:01'),(316,20241002210000,1,'2020-01-01 01:01:01'),(317,20241003145349,1,'2020-01-01 01:01:01'),(318,20241004005000,1,'2020-01-01 01:01:01'),(319,20241008083925,1,'2020-01-01 01:01:01'),(320,20241009090010,1,'2020-01-01 01:01:01'),(321,20241017163402,1,'2020-01-01 01:01:01'),(322,20241021224359,1,'2020-01-01 01:01:01'),(323,20241022140321,1,'2020-01-01 01:01:01'),(324,20241025111236,1,'2020-01-01 01:01:01'),(325,20241025112748,1,'2020-01-01 01:01:01'),(326,20241025141855,1,'2020-01-01 01:01:01'),(327,20241110152839,1,'2020-01-01 01:01:01'),(328,20241110152840,1,'2020-01-01 01:01:01'),(329,20241110152841,1,'2020-01-01 01:01:01'),(330,20241116233322,1,'2020-01-01 01:01:01'),(331,20241122171434,1,'2020-01-01 01:01:01'),(332,20241125150614,1,'2020-01-01 01:01:01'),(333,20241203125346,1,'2020-01-01 01:01:01'),(334,20241203130032,1,'2020-01-01 01:01:01'),(335,20241205122800,1,'2020-01-01 01:01:01'),(336,20241209164540,1,'2020-01-01 01:01:01'),(337,20241210140021,1,'2020-01-01 01:01:01'),(338,20241219180042,1,'2020-01-01 01:01:01'),(339,20241220100000,1,'2020-01-01 01:01:01'),(340,20241220114903,1,'2020-01-01 01:01:01'),(341,20241220114904,1,'2020-01-01 01:01:01'),(342,20241224000000,1,'2020-01-01 01:01:01'),(343,20241230000000,1,'2020-01-01 01:01:01'),(344,20241231112624,1,'2020-01-01 01:01:01'),(345,20250102121439,1,'2020-01-01 01:01:01'); +INSERT INTO `migration_status_tables` VALUES (1,0,1,'2020-01-01 01:01:01'),(2,20161118193812,1,'2020-01-01 01:01:01'),(3,20161118211713,1,'2020-01-01 01:01:01'),(4,20161118212436,1,'2020-01-01 01:01:01'),(5,20161118212515,1,'2020-01-01 01:01:01'),(6,20161118212528,1,'2020-01-01 01:01:01'),(7,20161118212538,1,'2020-01-01 01:01:01'),(8,20161118212549,1,'2020-01-01 01:01:01'),(9,20161118212557,1,'2020-01-01 01:01:01'),(10,20161118212604,1,'2020-01-01 01:01:01'),(11,20161118212613,1,'2020-01-01 01:01:01'),(12,20161118212621,1,'2020-01-01 01:01:01'),(13,20161118212630,1,'2020-01-01 01:01:01'),(14,20161118212641,1,'2020-01-01 01:01:01'),(15,20161118212649,1,'2020-01-01 01:01:01'),(16,20161118212656,1,'2020-01-01 01:01:01'),(17,20161118212758,1,'2020-01-01 01:01:01'),(18,20161128234849,1,'2020-01-01 01:01:01'),(19,20161230162221,1,'2020-01-01 01:01:01'),(20,20170104113816,1,'2020-01-01 01:01:01'),(21,20170105151732,1,'2020-01-01 01:01:01'),(22,20170108191242,1,'2020-01-01 01:01:01'),(23,20170109094020,1,'2020-01-01 01:01:01'),(24,20170109130438,1,'2020-01-01 01:01:01'),(25,20170110202752,1,'2020-01-01 01:01:01'),(26,20170111133013,1,'2020-01-01 01:01:01'),(27,20170117025759,1,'2020-01-01 01:01:01'),(28,20170118191001,1,'2020-01-01 01:01:01'),(29,20170119234632,1,'2020-01-01 01:01:01'),(30,20170124230432,1,'2020-01-01 01:01:01'),(31,20170127014618,1,'2020-01-01 01:01:01'),(32,20170131232841,1,'2020-01-01 01:01:01'),(33,20170223094154,1,'2020-01-01 01:01:01'),(34,20170306075207,1,'2020-01-01 01:01:01'),(35,20170309100733,1,'2020-01-01 01:01:01'),(36,20170331111922,1,'2020-01-01 01:01:01'),(37,20170502143928,1,'2020-01-01 01:01:01'),(38,20170504130602,1,'2020-01-01 01:01:01'),(39,20170509132100,1,'2020-01-01 01:01:01'),(40,20170519105647,1,'2020-01-01 01:01:01'),(41,20170519105648,1,'2020-01-01 01:01:01'),(42,20170831234300,1,'2020-01-01 01:01:01'),(43,20170831234301,1,'2020-01-01 01:01:01'),(44,20170831234303,1,'2020-01-01 01:01:01'),(45,20171116163618,1,'2020-01-01 01:01:01'),(46,20171219164727,1,'2020-01-01 01:01:01'),(47,20180620164811,1,'2020-01-01 01:01:01'),(48,20180620175054,1,'2020-01-01 01:01:01'),(49,20180620175055,1,'2020-01-01 01:01:01'),(50,20191010101639,1,'2020-01-01 01:01:01'),(51,20191010155147,1,'2020-01-01 01:01:01'),(52,20191220130734,1,'2020-01-01 01:01:01'),(53,20200311140000,1,'2020-01-01 01:01:01'),(54,20200405120000,1,'2020-01-01 01:01:01'),(55,20200407120000,1,'2020-01-01 01:01:01'),(56,20200420120000,1,'2020-01-01 01:01:01'),(57,20200504120000,1,'2020-01-01 01:01:01'),(58,20200512120000,1,'2020-01-01 01:01:01'),(59,20200707120000,1,'2020-01-01 01:01:01'),(60,20201011162341,1,'2020-01-01 01:01:01'),(61,20201021104586,1,'2020-01-01 01:01:01'),(62,20201102112520,1,'2020-01-01 01:01:01'),(63,20201208121729,1,'2020-01-01 01:01:01'),(64,20201215091637,1,'2020-01-01 01:01:01'),(65,20210119174155,1,'2020-01-01 01:01:01'),(66,20210326182902,1,'2020-01-01 01:01:01'),(67,20210421112652,1,'2020-01-01 01:01:01'),(68,20210506095025,1,'2020-01-01 01:01:01'),(69,20210513115729,1,'2020-01-01 01:01:01'),(70,20210526113559,1,'2020-01-01 01:01:01'),(71,20210601000001,1,'2020-01-01 01:01:01'),(72,20210601000002,1,'2020-01-01 01:01:01'),(73,20210601000003,1,'2020-01-01 01:01:01'),(74,20210601000004,1,'2020-01-01 01:01:01'),(75,20210601000005,1,'2020-01-01 01:01:01'),(76,20210601000006,1,'2020-01-01 01:01:01'),(77,20210601000007,1,'2020-01-01 01:01:01'),(78,20210601000008,1,'2020-01-01 01:01:01'),(79,20210606151329,1,'2020-01-01 01:01:01'),(80,20210616163757,1,'2020-01-01 01:01:01'),(81,20210617174723,1,'2020-01-01 01:01:01'),(82,20210622160235,1,'2020-01-01 01:01:01'),(83,20210623100031,1,'2020-01-01 01:01:01'),(84,20210623133615,1,'2020-01-01 01:01:01'),(85,20210708143152,1,'2020-01-01 01:01:01'),(86,20210709124443,1,'2020-01-01 01:01:01'),(87,20210712155608,1,'2020-01-01 01:01:01'),(88,20210714102108,1,'2020-01-01 01:01:01'),(89,20210719153709,1,'2020-01-01 01:01:01'),(90,20210721171531,1,'2020-01-01 01:01:01'),(91,20210723135713,1,'2020-01-01 01:01:01'),(92,20210802135933,1,'2020-01-01 01:01:01'),(93,20210806112844,1,'2020-01-01 01:01:01'),(94,20210810095603,1,'2020-01-01 01:01:01'),(95,20210811150223,1,'2020-01-01 01:01:01'),(96,20210818151827,1,'2020-01-01 01:01:01'),(97,20210818151828,1,'2020-01-01 01:01:01'),(98,20210818182258,1,'2020-01-01 01:01:01'),(99,20210819131107,1,'2020-01-01 01:01:01'),(100,20210819143446,1,'2020-01-01 01:01:01'),(101,20210903132338,1,'2020-01-01 01:01:01'),(102,20210915144307,1,'2020-01-01 01:01:01'),(103,20210920155130,1,'2020-01-01 01:01:01'),(104,20210927143115,1,'2020-01-01 01:01:01'),(105,20210927143116,1,'2020-01-01 01:01:01'),(106,20211013133706,1,'2020-01-01 01:01:01'),(107,20211013133707,1,'2020-01-01 01:01:01'),(108,20211102135149,1,'2020-01-01 01:01:01'),(109,20211109121546,1,'2020-01-01 01:01:01'),(110,20211110163320,1,'2020-01-01 01:01:01'),(111,20211116184029,1,'2020-01-01 01:01:01'),(112,20211116184030,1,'2020-01-01 01:01:01'),(113,20211202092042,1,'2020-01-01 01:01:01'),(114,20211202181033,1,'2020-01-01 01:01:01'),(115,20211207161856,1,'2020-01-01 01:01:01'),(116,20211216131203,1,'2020-01-01 01:01:01'),(117,20211221110132,1,'2020-01-01 01:01:01'),(118,20220107155700,1,'2020-01-01 01:01:01'),(119,20220125105650,1,'2020-01-01 01:01:01'),(120,20220201084510,1,'2020-01-01 01:01:01'),(121,20220208144830,1,'2020-01-01 01:01:01'),(122,20220208144831,1,'2020-01-01 01:01:01'),(123,20220215152203,1,'2020-01-01 01:01:01'),(124,20220223113157,1,'2020-01-01 01:01:01'),(125,20220307104655,1,'2020-01-01 01:01:01'),(126,20220309133956,1,'2020-01-01 01:01:01'),(127,20220316155700,1,'2020-01-01 01:01:01'),(128,20220323152301,1,'2020-01-01 01:01:01'),(129,20220330100659,1,'2020-01-01 01:01:01'),(130,20220404091216,1,'2020-01-01 01:01:01'),(131,20220419140750,1,'2020-01-01 01:01:01'),(132,20220428140039,1,'2020-01-01 01:01:01'),(133,20220503134048,1,'2020-01-01 01:01:01'),(134,20220524102918,1,'2020-01-01 01:01:01'),(135,20220526123327,1,'2020-01-01 01:01:01'),(136,20220526123328,1,'2020-01-01 01:01:01'),(137,20220526123329,1,'2020-01-01 01:01:01'),(138,20220608113128,1,'2020-01-01 01:01:01'),(139,20220627104817,1,'2020-01-01 01:01:01'),(140,20220704101843,1,'2020-01-01 01:01:01'),(141,20220708095046,1,'2020-01-01 01:01:01'),(142,20220713091130,1,'2020-01-01 01:01:01'),(143,20220802135510,1,'2020-01-01 01:01:01'),(144,20220818101352,1,'2020-01-01 01:01:01'),(145,20220822161445,1,'2020-01-01 01:01:01'),(146,20220831100036,1,'2020-01-01 01:01:01'),(147,20220831100151,1,'2020-01-01 01:01:01'),(148,20220908181826,1,'2020-01-01 01:01:01'),(149,20220914154915,1,'2020-01-01 01:01:01'),(150,20220915165115,1,'2020-01-01 01:01:01'),(151,20220915165116,1,'2020-01-01 01:01:01'),(152,20220928100158,1,'2020-01-01 01:01:01'),(153,20221014084130,1,'2020-01-01 01:01:01'),(154,20221027085019,1,'2020-01-01 01:01:01'),(155,20221101103952,1,'2020-01-01 01:01:01'),(156,20221104144401,1,'2020-01-01 01:01:01'),(157,20221109100749,1,'2020-01-01 01:01:01'),(158,20221115104546,1,'2020-01-01 01:01:01'),(159,20221130114928,1,'2020-01-01 01:01:01'),(160,20221205112142,1,'2020-01-01 01:01:01'),(161,20221216115820,1,'2020-01-01 01:01:01'),(162,20221220195934,1,'2020-01-01 01:01:01'),(163,20221220195935,1,'2020-01-01 01:01:01'),(164,20221223174807,1,'2020-01-01 01:01:01'),(165,20221227163855,1,'2020-01-01 01:01:01'),(166,20221227163856,1,'2020-01-01 01:01:01'),(167,20230202224725,1,'2020-01-01 01:01:01'),(168,20230206163608,1,'2020-01-01 01:01:01'),(169,20230214131519,1,'2020-01-01 01:01:01'),(170,20230303135738,1,'2020-01-01 01:01:01'),(171,20230313135301,1,'2020-01-01 01:01:01'),(172,20230313141819,1,'2020-01-01 01:01:01'),(173,20230315104937,1,'2020-01-01 01:01:01'),(174,20230317173844,1,'2020-01-01 01:01:01'),(175,20230320133602,1,'2020-01-01 01:01:01'),(176,20230330100011,1,'2020-01-01 01:01:01'),(177,20230330134823,1,'2020-01-01 01:01:01'),(178,20230405232025,1,'2020-01-01 01:01:01'),(179,20230408084104,1,'2020-01-01 01:01:01'),(180,20230411102858,1,'2020-01-01 01:01:01'),(181,20230421155932,1,'2020-01-01 01:01:01'),(182,20230425082126,1,'2020-01-01 01:01:01'),(183,20230425105727,1,'2020-01-01 01:01:01'),(184,20230501154913,1,'2020-01-01 01:01:01'),(185,20230503101418,1,'2020-01-01 01:01:01'),(186,20230515144206,1,'2020-01-01 01:01:01'),(187,20230517140952,1,'2020-01-01 01:01:01'),(188,20230517152807,1,'2020-01-01 01:01:01'),(189,20230518114155,1,'2020-01-01 01:01:01'),(190,20230520153236,1,'2020-01-01 01:01:01'),(191,20230525151159,1,'2020-01-01 01:01:01'),(192,20230530122103,1,'2020-01-01 01:01:01'),(193,20230602111827,1,'2020-01-01 01:01:01'),(194,20230608103123,1,'2020-01-01 01:01:01'),(195,20230629140529,1,'2020-01-01 01:01:01'),(196,20230629140530,1,'2020-01-01 01:01:01'),(197,20230711144622,1,'2020-01-01 01:01:01'),(198,20230721135421,1,'2020-01-01 01:01:01'),(199,20230721161508,1,'2020-01-01 01:01:01'),(200,20230726115701,1,'2020-01-01 01:01:01'),(201,20230807100822,1,'2020-01-01 01:01:01'),(202,20230814150442,1,'2020-01-01 01:01:01'),(203,20230823122728,1,'2020-01-01 01:01:01'),(204,20230906152143,1,'2020-01-01 01:01:01'),(205,20230911163618,1,'2020-01-01 01:01:01'),(206,20230912101759,1,'2020-01-01 01:01:01'),(207,20230915101341,1,'2020-01-01 01:01:01'),(208,20230918132351,1,'2020-01-01 01:01:01'),(209,20231004144339,1,'2020-01-01 01:01:01'),(210,20231009094541,1,'2020-01-01 01:01:01'),(211,20231009094542,1,'2020-01-01 01:01:01'),(212,20231009094543,1,'2020-01-01 01:01:01'),(213,20231009094544,1,'2020-01-01 01:01:01'),(214,20231016091915,1,'2020-01-01 01:01:01'),(215,20231024174135,1,'2020-01-01 01:01:01'),(216,20231025120016,1,'2020-01-01 01:01:01'),(217,20231025160156,1,'2020-01-01 01:01:01'),(218,20231031165350,1,'2020-01-01 01:01:01'),(219,20231106144110,1,'2020-01-01 01:01:01'),(220,20231107130934,1,'2020-01-01 01:01:01'),(221,20231109115838,1,'2020-01-01 01:01:01'),(222,20231121054530,1,'2020-01-01 01:01:01'),(223,20231122101320,1,'2020-01-01 01:01:01'),(224,20231130132828,1,'2020-01-01 01:01:01'),(225,20231130132931,1,'2020-01-01 01:01:01'),(226,20231204155427,1,'2020-01-01 01:01:01'),(227,20231206142340,1,'2020-01-01 01:01:01'),(228,20231207102320,1,'2020-01-01 01:01:01'),(229,20231207102321,1,'2020-01-01 01:01:01'),(230,20231207133731,1,'2020-01-01 01:01:01'),(231,20231212094238,1,'2020-01-01 01:01:01'),(232,20231212095734,1,'2020-01-01 01:01:01'),(233,20231212161121,1,'2020-01-01 01:01:01'),(234,20231215122713,1,'2020-01-01 01:01:01'),(235,20231219143041,1,'2020-01-01 01:01:01'),(236,20231224070653,1,'2020-01-01 01:01:01'),(237,20240110134315,1,'2020-01-01 01:01:01'),(238,20240119091637,1,'2020-01-01 01:01:01'),(239,20240126020642,1,'2020-01-01 01:01:01'),(240,20240126020643,1,'2020-01-01 01:01:01'),(241,20240129162819,1,'2020-01-01 01:01:01'),(242,20240130115133,1,'2020-01-01 01:01:01'),(243,20240131083822,1,'2020-01-01 01:01:01'),(244,20240205095928,1,'2020-01-01 01:01:01'),(245,20240205121956,1,'2020-01-01 01:01:01'),(246,20240209110212,1,'2020-01-01 01:01:01'),(247,20240212111533,1,'2020-01-01 01:01:01'),(248,20240221112844,1,'2020-01-01 01:01:01'),(249,20240222073518,1,'2020-01-01 01:01:01'),(250,20240222135115,1,'2020-01-01 01:01:01'),(251,20240226082255,1,'2020-01-01 01:01:01'),(252,20240228082706,1,'2020-01-01 01:01:01'),(253,20240301173035,1,'2020-01-01 01:01:01'),(254,20240302111134,1,'2020-01-01 01:01:01'),(255,20240312103753,1,'2020-01-01 01:01:01'),(256,20240313143416,1,'2020-01-01 01:01:01'),(257,20240314085226,1,'2020-01-01 01:01:01'),(258,20240314151747,1,'2020-01-01 01:01:01'),(259,20240320145650,1,'2020-01-01 01:01:01'),(260,20240327115530,1,'2020-01-01 01:01:01'),(261,20240327115617,1,'2020-01-01 01:01:01'),(262,20240408085837,1,'2020-01-01 01:01:01'),(263,20240415104633,1,'2020-01-01 01:01:01'),(264,20240430111727,1,'2020-01-01 01:01:01'),(265,20240515200020,1,'2020-01-01 01:01:01'),(266,20240521143023,1,'2020-01-01 01:01:01'),(267,20240521143024,1,'2020-01-01 01:01:01'),(268,20240601174138,1,'2020-01-01 01:01:01'),(269,20240607133721,1,'2020-01-01 01:01:01'),(270,20240612150059,1,'2020-01-01 01:01:01'),(271,20240613162201,1,'2020-01-01 01:01:01'),(272,20240613172616,1,'2020-01-01 01:01:01'),(273,20240618142419,1,'2020-01-01 01:01:01'),(274,20240625093543,1,'2020-01-01 01:01:01'),(275,20240626195531,1,'2020-01-01 01:01:01'),(276,20240702123921,1,'2020-01-01 01:01:01'),(277,20240703154849,1,'2020-01-01 01:01:01'),(278,20240707134035,1,'2020-01-01 01:01:01'),(279,20240707134036,1,'2020-01-01 01:01:01'),(280,20240709124958,1,'2020-01-01 01:01:01'),(281,20240709132642,1,'2020-01-01 01:01:01'),(282,20240709183940,1,'2020-01-01 01:01:01'),(283,20240710155623,1,'2020-01-01 01:01:01'),(284,20240723102712,1,'2020-01-01 01:01:01'),(285,20240725152735,1,'2020-01-01 01:01:01'),(286,20240725182118,1,'2020-01-01 01:01:01'),(287,20240726100517,1,'2020-01-01 01:01:01'),(288,20240730171504,1,'2020-01-01 01:01:01'),(289,20240730174056,1,'2020-01-01 01:01:01'),(290,20240730215453,1,'2020-01-01 01:01:01'),(291,20240730374423,1,'2020-01-01 01:01:01'),(292,20240801115359,1,'2020-01-01 01:01:01'),(293,20240802101043,1,'2020-01-01 01:01:01'),(294,20240802113716,1,'2020-01-01 01:01:01'),(295,20240814135330,1,'2020-01-01 01:01:01'),(296,20240815000000,1,'2020-01-01 01:01:01'),(297,20240815000001,1,'2020-01-01 01:01:01'),(298,20240816103247,1,'2020-01-01 01:01:01'),(299,20240820091218,1,'2020-01-01 01:01:01'),(300,20240826111228,1,'2020-01-01 01:01:01'),(301,20240826160025,1,'2020-01-01 01:01:01'),(302,20240829165448,1,'2020-01-01 01:01:01'),(303,20240829165605,1,'2020-01-01 01:01:01'),(304,20240829165715,1,'2020-01-01 01:01:01'),(305,20240829165930,1,'2020-01-01 01:01:01'),(306,20240829170023,1,'2020-01-01 01:01:01'),(307,20240829170033,1,'2020-01-01 01:01:01'),(308,20240829170044,1,'2020-01-01 01:01:01'),(309,20240905105135,1,'2020-01-01 01:01:01'),(310,20240905140514,1,'2020-01-01 01:01:01'),(311,20240905200000,1,'2020-01-01 01:01:01'),(312,20240905200001,1,'2020-01-01 01:01:01'),(313,20241002104104,1,'2020-01-01 01:01:01'),(314,20241002104105,1,'2020-01-01 01:01:01'),(315,20241002104106,1,'2020-01-01 01:01:01'),(316,20241002210000,1,'2020-01-01 01:01:01'),(317,20241003145349,1,'2020-01-01 01:01:01'),(318,20241004005000,1,'2020-01-01 01:01:01'),(319,20241008083925,1,'2020-01-01 01:01:01'),(320,20241009090010,1,'2020-01-01 01:01:01'),(321,20241017163402,1,'2020-01-01 01:01:01'),(322,20241021224359,1,'2020-01-01 01:01:01'),(323,20241022140321,1,'2020-01-01 01:01:01'),(324,20241025111236,1,'2020-01-01 01:01:01'),(325,20241025112748,1,'2020-01-01 01:01:01'),(326,20241025141855,1,'2020-01-01 01:01:01'),(327,20241110152839,1,'2020-01-01 01:01:01'),(328,20241110152840,1,'2020-01-01 01:01:01'),(329,20241110152841,1,'2020-01-01 01:01:01'),(330,20241116233322,1,'2020-01-01 01:01:01'),(331,20241122171434,1,'2020-01-01 01:01:01'),(332,20241125150614,1,'2020-01-01 01:01:01'),(333,20241203125346,1,'2020-01-01 01:01:01'),(334,20241203130032,1,'2020-01-01 01:01:01'),(335,20241205122800,1,'2020-01-01 01:01:01'),(336,20241209164540,1,'2020-01-01 01:01:01'),(337,20241210140021,1,'2020-01-01 01:01:01'),(338,20241219180042,1,'2020-01-01 01:01:01'),(339,20241220100000,1,'2020-01-01 01:01:01'),(340,20241220114903,1,'2020-01-01 01:01:01'),(341,20241220114904,1,'2020-01-01 01:01:01'),(342,20241224000000,1,'2020-01-01 01:01:01'),(343,20241230000000,1,'2020-01-01 01:01:01'),(344,20241231112624,1,'2020-01-01 01:01:01'),(345,20250102121439,1,'2020-01-01 01:01:01'),(346,20250107165731,1,'2020-01-01 01:01:01'); /*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; CREATE TABLE `mobile_device_management_solutions` ( @@ -1938,6 +1938,7 @@ CREATE TABLE `users` ( `global_role` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL, `api_only` tinyint(1) NOT NULL DEFAULT '0', `mfa_enabled` tinyint(1) NOT NULL DEFAULT '0', + `settings` json NOT NULL DEFAULT (json_object()), PRIMARY KEY (`id`), UNIQUE KEY `idx_user_unique_email` (`email`) ) /*!50100 TABLESPACE `innodb_system` */ ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/server/datastore/mysql/users.go b/server/datastore/mysql/users.go index faecf821dc40..a32eeee85036 100644 --- a/server/datastore/mysql/users.go +++ b/server/datastore/mysql/users.go @@ -3,6 +3,7 @@ package mysql import ( "context" "database/sql" + "encoding/json" "fmt" "strings" "time" @@ -74,7 +75,11 @@ func (ds *Datastore) NewUser(ctx context.Context, user *fleet.User) (*fleet.User func (ds *Datastore) findUser(ctx context.Context, searchCol string, searchVal interface{}) (*fleet.User, error) { sqlStatement := fmt.Sprintf( - "SELECT * FROM users "+ + // everything except `settings`. Since we only want to include user settings on an opt-in basis + // from the API perspective (see `include_ui_settings` query param on `GET` `/me` and `GET` `/users/:id`), excluding it here ensures it's only included in API responses + // when explicitly coded to be, via calling the dedicated UserSettings method. Otherwise, + // `settings` would be included in `user` objects in various places, which we do not want. + "SELECT id, created_at, updated_at, password, salt, name, email, admin_forced_password_reset, gravatar_url, position, sso_enabled, global_role, api_only, mfa_enabled FROM users "+ "WHERE %s = ? LIMIT 1", searchCol, ) @@ -172,9 +177,14 @@ func saveUserDB(ctx context.Context, tx sqlx.ExtContext, user *fleet.User) error sso_enabled = ?, mfa_enabled = ?, api_only = ?, + settings = ?, global_role = ? WHERE id = ? ` + settingsBytes, err := json.Marshal(user.Settings) + if err != nil { + return ctxerr.Wrap(ctx, err, "marshaling user settings") + } result, err := tx.ExecContext(ctx, sqlStatement, user.Password, user.Salt, @@ -186,6 +196,7 @@ func saveUserDB(ctx context.Context, tx sqlx.ExtContext, user *fleet.User) error user.SSOEnabled, user.MFAEnabled, user.APIOnly, + settingsBytes, user.GlobalRole, user.ID) if err != nil { @@ -310,3 +321,28 @@ func amountActiveUsersSinceDB(ctx context.Context, db sqlx.QueryerContext, since } return amount, nil } + +func (ds *Datastore) UserSettings(ctx context.Context, userID uint) (*fleet.UserSettings, error) { + settings := &fleet.UserSettings{} + var bytes []byte + stmt := ` + SELECT settings FROM users WHERE id = ? + ` + err := sqlx.GetContext(ctx, ds.reader(ctx), &bytes, stmt, userID) + if err != nil { + if err == sql.ErrNoRows { + return nil, ctxerr.Wrap(ctx, notFound("UserSettings").WithID(userID)) + } + return nil, ctxerr.Wrap(ctx, err, "selecting user settings") + } + + if len(bytes) == 0 { + return settings, nil + } + + err = json.Unmarshal(bytes, settings) + if err != nil { + return nil, ctxerr.Wrap(ctx, err, "unmarshaling user settings") + } + return settings, nil +} diff --git a/server/datastore/mysql/users_test.go b/server/datastore/mysql/users_test.go index cabe8e9a7887..43779db60390 100644 --- a/server/datastore/mysql/users_test.go +++ b/server/datastore/mysql/users_test.go @@ -138,6 +138,7 @@ func testUsersSave(t *testing.T, ds *Datastore) { testEmailAttribute(t, ds, users) testPasswordAttribute(t, ds, users) testMFAAttribute(t, ds, users) + testSettingsAttribute(t, ds, users) } func testMFAAttribute(t *testing.T, ds fleet.Datastore, users []*fleet.User) { @@ -160,6 +161,28 @@ func testMFAAttribute(t *testing.T, ds fleet.Datastore, users []*fleet.User) { } } +func testSettingsAttribute(t *testing.T, ds fleet.Datastore, users []*fleet.User) { + for _, user := range users { + user.Settings = &fleet.UserSettings{} + err := ds.SaveUser(context.Background(), user) + assert.Nil(t, err) + + verify, err := ds.UserByID(context.Background(), user.ID) + assert.Nil(t, err) + // settings should only be returned via dedicated method + assert.Nil(t, verify.Settings) + + user.Settings.HiddenHostColumns = []string{"osquery_version"} + err = ds.SaveUser(context.Background(), user) + assert.Nil(t, err) + + // call the settings db method here + settings, err := ds.UserSettings(context.Background(), user.ID) + assert.Nil(t, err) + assert.Equal(t, settings.HiddenHostColumns, user.Settings.HiddenHostColumns) + } +} + func testPasswordAttribute(t *testing.T, ds fleet.Datastore, users []*fleet.User) { for _, user := range users { randomText, err := server.GenerateRandomText(8) // GenerateRandomText(8) diff --git a/server/fleet/app_test.go b/server/fleet/app_test.go index 7f70fe0c9a35..8456a21ae673 100644 --- a/server/fleet/app_test.go +++ b/server/fleet/app_test.go @@ -2,6 +2,7 @@ package fleet import ( "encoding/json" + "reflect" "testing" "github.com/fleetdm/fleet/v4/pkg/optjson" @@ -354,7 +355,10 @@ func TestFeaturesCopy(t *testing.T) { } clone := f.Copy() require.NotNil(t, clone.DetailQueryOverrides) - require.NotSame(t, f.DetailQueryOverrides, clone.DetailQueryOverrides) + require.NotEqual(t, + reflect.ValueOf(f.DetailQueryOverrides).Pointer(), + reflect.ValueOf(clone.DetailQueryOverrides).Pointer(), + ) // map values are pointers, check that they have been cloned require.NotSame(t, f.DetailQueryOverrides["foo"], clone.DetailQueryOverrides["foo"]) // the map content itself is equal diff --git a/server/fleet/datastore.go b/server/fleet/datastore.go index 8d9f173f55fa..019502b6ba93 100644 --- a/server/fleet/datastore.go +++ b/server/fleet/datastore.go @@ -68,6 +68,8 @@ type Datastore interface { // written to user record. userID is the ID of the user whose e-mail is being changed. ConfirmPendingEmailChange(ctx context.Context, userID uint, token string) (string, error) + UserSettings(ctx context.Context, userID uint) (*UserSettings, error) + /////////////////////////////////////////////////////////////////////////////// // QueryStore diff --git a/server/fleet/service.go b/server/fleet/service.go index 9c4175935790..ee0f6c6ad52d 100644 --- a/server/fleet/service.go +++ b/server/fleet/service.go @@ -162,6 +162,8 @@ type Service interface { // write the new email address to user. ChangeUserEmail(ctx context.Context, token string) (string, error) + GetUserSettings(ctx context.Context, id uint) (settings *UserSettings, err error) + // ///////////////////////////////////////////////////////////////////////////// // Session diff --git a/server/fleet/teams_test.go b/server/fleet/teams_test.go index a9de206787d2..7a20c934be3f 100644 --- a/server/fleet/teams_test.go +++ b/server/fleet/teams_test.go @@ -1,6 +1,7 @@ package fleet import ( + "reflect" "testing" "github.com/fleetdm/fleet/v4/pkg/optjson" @@ -295,7 +296,10 @@ func TestTeamMDMCopy(t *testing.T) { clone := tm.Copy() require.NotSame(t, tm, clone) require.Equal(t, tm, clone) - require.NotSame(t, tm.MacOSSettings.CustomSettings, clone.MacOSSettings.CustomSettings) + require.NotEqual(t, + reflect.ValueOf(tm.MacOSSettings.CustomSettings).Pointer(), + reflect.ValueOf(clone.MacOSSettings.CustomSettings).Pointer(), + ) require.NotSame(t, tm.MacOSSettings.DeprecatedEnableDiskEncryption, clone.MacOSSettings.DeprecatedEnableDiskEncryption) }) } diff --git a/server/fleet/users.go b/server/fleet/users.go index 8d6a3143646b..a8b04fbb5779 100644 --- a/server/fleet/users.go +++ b/server/fleet/users.go @@ -32,6 +32,25 @@ type User struct { // Teams is the teams this user has roles in. For users with a global role, Teams is expected to be empty. Teams []UserTeam `json:"teams"` + + Settings *UserSettings `json:"settings,omitempty"` +} + +type UserSettings struct { + HiddenHostColumns []string `json:"hidden_host_columns"` +} + +// Scan implements the sql.Scanner interface, tells DB driver how to convert MySQL type (json) to +// custom Go type (UserSettings). +func (us *UserSettings) Scan(val interface{}) error { + switch v := val.(type) { + case []byte: + return json.Unmarshal(v, us) + case string: + return json.Unmarshal([]byte(v), us) + default: + return fmt.Errorf("unsupported type: %T", v) + } } // IsGlobalObserver returns true if user is either a Global Observer or a Global Observer+ @@ -149,20 +168,21 @@ type UserListOptions struct { // UserPayload is used to modify an existing user type UserPayload struct { - Name *string `json:"name,omitempty"` - Email *string `json:"email,omitempty"` - Password *string `json:"password,omitempty"` - GravatarURL *string `json:"gravatar_url,omitempty"` - Position *string `json:"position,omitempty"` - InviteToken *string `json:"invite_token,omitempty"` - SSOInvite *bool `json:"sso_invite,omitempty"` - MFAEnabled *bool `json:"mfa_enabled,omitempty"` - SSOEnabled *bool `json:"sso_enabled,omitempty"` - GlobalRole *string `json:"global_role,omitempty"` - AdminForcedPasswordReset *bool `json:"admin_forced_password_reset,omitempty"` - APIOnly *bool `json:"api_only,omitempty"` - Teams *[]UserTeam `json:"teams,omitempty"` - NewPassword *string `json:"new_password,omitempty"` + Name *string `json:"name,omitempty"` + Email *string `json:"email,omitempty"` + Password *string `json:"password,omitempty"` + GravatarURL *string `json:"gravatar_url,omitempty"` + Position *string `json:"position,omitempty"` + InviteToken *string `json:"invite_token,omitempty"` + SSOInvite *bool `json:"sso_invite,omitempty"` + MFAEnabled *bool `json:"mfa_enabled,omitempty"` + SSOEnabled *bool `json:"sso_enabled,omitempty"` + GlobalRole *string `json:"global_role,omitempty"` + AdminForcedPasswordReset *bool `json:"admin_forced_password_reset,omitempty"` + APIOnly *bool `json:"api_only,omitempty"` + Teams *[]UserTeam `json:"teams,omitempty"` + NewPassword *string `json:"new_password,omitempty"` + Settings *UserSettings `json:"settings,omitempty"` } func (p *UserPayload) VerifyInviteCreate() error { diff --git a/server/mock/datastore_mock.go b/server/mock/datastore_mock.go index b686b20370ad..257c9bf4938d 100644 --- a/server/mock/datastore_mock.go +++ b/server/mock/datastore_mock.go @@ -49,6 +49,8 @@ type UserByEmailFunc func(ctx context.Context, email string) (*fleet.User, error type UserByIDFunc func(ctx context.Context, id uint) (*fleet.User, error) +type UserSettingsFunc func(ctx context.Context, userID uint) (*fleet.UserSettings, error) + type SaveUserFunc func(ctx context.Context, user *fleet.User) error type SaveUsersFunc func(ctx context.Context, users []*fleet.User) error @@ -1236,6 +1238,9 @@ type DataStore struct { UserByIDFunc UserByIDFunc UserByIDFuncInvoked bool + UserSettingsFunc UserSettingsFunc + UserSettingsFuncInvoked bool + SaveUserFunc SaveUserFunc SaveUserFuncInvoked bool @@ -3053,6 +3058,13 @@ func (s *DataStore) UserByID(ctx context.Context, id uint) (*fleet.User, error) return s.UserByIDFunc(ctx, id) } +func (s *DataStore) UserSettings(ctx context.Context, userID uint) (*fleet.UserSettings, error) { + s.mu.Lock() + s.UserSettingsFuncInvoked = true + s.mu.Unlock() + return s.UserSettingsFunc(ctx, userID) +} + func (s *DataStore) SaveUser(ctx context.Context, user *fleet.User) error { s.mu.Lock() s.SaveUserFuncInvoked = true diff --git a/server/service/apple_mdm.go b/server/service/apple_mdm.go index 50758be0cd87..0d2965c99713 100644 --- a/server/service/apple_mdm.go +++ b/server/service/apple_mdm.go @@ -2376,7 +2376,13 @@ func (r bootstrapPackageMetadataResponse) error() error { return r.Err } func bootstrapPackageMetadataEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (errorer, error) { req := request.(*bootstrapPackageMetadataRequest) meta, err := svc.GetMDMAppleBootstrapPackageMetadata(ctx, req.TeamID, req.ForUpdate) - if err != nil { + switch { + case fleet.IsNotFound(err): + // Don't log this response as error -- it's expected to happen when the bootstrap package is missing, which is a common case. + logging.WithNoError(ctx) + return bootstrapPackageMetadataResponse{Err: fleet.NewInvalidArgumentError("team_id", + "bootstrap package for this team does not exist").WithStatus(http.StatusNotFound)}, nil + case err != nil: return bootstrapPackageMetadataResponse{Err: err}, nil } return bootstrapPackageMetadataResponse{MDMAppleBootstrapPackage: meta}, nil diff --git a/server/service/handler.go b/server/service/handler.go index 997db84c2c5b..005884a21bea 100644 --- a/server/service/handler.go +++ b/server/service/handler.go @@ -265,7 +265,7 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC ue.POST("/api/_version_/fleet/trigger", triggerEndpoint, triggerRequest{}) - ue.GET("/api/_version_/fleet/me", meEndpoint, nil) + ue.GET("/api/_version_/fleet/me", meEndpoint, getMeRequest{}) ue.GET("/api/_version_/fleet/sessions/{id:[0-9]+}", getInfoAboutSessionEndpoint, getInfoAboutSessionRequest{}) ue.DELETE("/api/_version_/fleet/sessions/{id:[0-9]+}", deleteSessionEndpoint, deleteSessionRequest{}) diff --git a/server/service/integration_core_test.go b/server/service/integration_core_test.go index 6aa261e19ced..97a78e1d404d 100644 --- a/server/service/integration_core_test.go +++ b/server/service/integration_core_test.go @@ -4655,6 +4655,93 @@ func (s *integrationTestSuite) TestUsers() { assert.Len(t, getMeResp.User.Teams, 0) assert.Len(t, getMeResp.AvailableTeams, 0) + // test user settings from 2 endpoints + + // get session user with ui settings, which should be empty, two endpoints + var getResp getUserResponse + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/users/%d", 1), nil, http.StatusOK, &getResp, "include_ui_settings", "true") + assert.Equal(t, uint(1), getResp.User.ID) + assert.Empty(t, getResp.User.Settings) + + resp = s.DoRawWithHeaders("GET", "/api/latest/fleet/me", []byte(""), http.StatusOK, map[string]string{ + "Authorization": fmt.Sprintf("Bearer %s", ssn.Key), + }, "include_ui_settings", "true") + err = json.NewDecoder(resp.Body).Decode(&getMeResp) + require.NoError(t, err) + // session user id 1 + assert.Equal(t, uint(1), getMeResp.User.ID) + assert.NotNil(t, getMeResp.User.GlobalRole) + // settings should only be present in dedicated settings field, not in user object + assert.Nil(t, getMeResp.User.Settings) + assert.Empty(t, getMeResp.Settings) + + // modify session user - add ui setting + var modResp modifyUserResponse + s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/users/%d", 1), json.RawMessage(`{ + "settings": { + "hidden_host_columns": ["osquery_version"]} + }`), http.StatusOK, &modResp) + + // get session user with ui settings, should now be present, two endpoints + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/users/%d", 1), nil, http.StatusOK, &getResp, "include_ui_settings", "true") + assert.Equal(t, uint(1), getResp.User.ID) + assert.Nil(t, getResp.User.Settings) + assert.Equal(t, getResp.Settings, &fleet.UserSettings{HiddenHostColumns: []string{"osquery_version"}}) + + resp = s.DoRawWithHeaders("GET", "/api/latest/fleet/me", []byte(""), http.StatusOK, map[string]string{ + "Authorization": fmt.Sprintf("Bearer %s", ssn.Key), + }, "include_ui_settings", "true") + err = json.NewDecoder(resp.Body).Decode(&getMeResp) + require.NoError(t, err) + assert.Equal(t, uint(1), getMeResp.User.ID) + assert.NotNil(t, getMeResp.User.GlobalRole) + assert.Nil(t, getMeResp.User.Settings) + assert.Equal(t, getResp.Settings, &fleet.UserSettings{HiddenHostColumns: []string{"osquery_version"}}) + + // modify user ui settings, check they are returned modified + s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/users/%d", 1), json.RawMessage(`{ + "settings": { + "hidden_host_columns": ["hostname", "osquery_version"]} + }`), http.StatusOK, &modResp) + + // get session user with ui settings, should now be modified, two endpoints + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/users/%d", 1), nil, http.StatusOK, &getResp, "include_ui_settings", "true") + assert.Equal(t, uint(1), getResp.User.ID) + assert.Nil(t, getResp.User.Settings) + assert.Equal(t, getResp.Settings, &fleet.UserSettings{HiddenHostColumns: []string{"hostname", "osquery_version"}}) + + resp = s.DoRawWithHeaders("GET", "/api/latest/fleet/me", []byte(""), http.StatusOK, map[string]string{ + "Authorization": fmt.Sprintf("Bearer %s", ssn.Key), + }, "include_ui_settings", "true") + err = json.NewDecoder(resp.Body).Decode(&getMeResp) + require.NoError(t, err) + assert.Equal(t, uint(1), getMeResp.User.ID) + assert.NotNil(t, getMeResp.User.GlobalRole) + assert.Nil(t, getMeResp.User.Settings) + assert.Equal(t, getMeResp.Settings, &fleet.UserSettings{HiddenHostColumns: []string{"hostname", "osquery_version"}}) + + // modify user ui settings, empty array, check they are returned correctly + s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/users/%d", 1), json.RawMessage(`{ + "settings": { + "hidden_host_columns": []} + }`), http.StatusOK, &modResp) + + // get session user with ui settings, should now be modified, two endpoints + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/users/%d", 1), nil, http.StatusOK, &getResp, "include_ui_settings", "true") + assert.Equal(t, uint(1), getResp.User.ID) + assert.Nil(t, getResp.User.Settings) + assert.Equal(t, getResp.Settings, &fleet.UserSettings{HiddenHostColumns: []string{}}) + + resp = s.DoRawWithHeaders("GET", "/api/latest/fleet/me", []byte(""), http.StatusOK, map[string]string{ + "Authorization": fmt.Sprintf("Bearer %s", ssn.Key), + }, "include_ui_settings", "true") + err = json.NewDecoder(resp.Body).Decode(&getMeResp) + require.NoError(t, err) + assert.Equal(t, uint(1), getMeResp.User.ID) + assert.NotNil(t, getMeResp.User.GlobalRole) + assert.Nil(t, getMeResp.User.Settings) + assert.Equal(t, getMeResp.Settings, &fleet.UserSettings{HiddenHostColumns: []string{}}) + // create a new user var createResp createUserResponse userRawPwd := test.GoodPassword @@ -4712,7 +4799,6 @@ func (s *integrationTestSuite) TestUsers() { s.DoJSONWithoutAuth("POST", "/api/latest/fleet/sessions", sessionCreateRequest{Token: mfaToken}, http.StatusUnauthorized, &loginResp) // turn off MFA - var modResp modifyUserResponse s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/users/%d", u.ID), fleet.UserPayload{MFAEnabled: ptr.Bool(false)}, http.StatusOK, &modResp) require.False(t, modResp.User.MFAEnabled) @@ -4726,7 +4812,6 @@ func (s *integrationTestSuite) TestUsers() { assert.Len(t, loginResp.AvailableTeams, 0) // get that user from `/users` endpoint and check that teams info is empty - var getResp getUserResponse s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/users/%d", u.ID), nil, http.StatusOK, &getResp) assert.Equal(t, u.ID, getResp.User.ID) assert.Len(t, getResp.User.Teams, 0) diff --git a/server/service/integration_logger_test.go b/server/service/integration_logger_test.go index 6a6f7be93147..307801c09edb 100644 --- a/server/service/integration_logger_test.go +++ b/server/service/integration_logger_test.go @@ -148,7 +148,7 @@ func (s *integrationLoggerTestSuite) TestLoggerLogin() { require.NotContains(t, logData, "user") // logger context is set to skip user for _, e := range tt.expectedLogs { - assert.Equal(t, logData[e.key], e.val) + assert.Equal(t, e.val, logData[e.key], fmt.Sprintf("%+v", tt.expectedLogs)) } s.buf.Reset() } diff --git a/server/service/users.go b/server/service/users.go index 96743c2ad669..b80726631302 100644 --- a/server/service/users.go +++ b/server/service/users.go @@ -223,9 +223,12 @@ func (svc *Service) ListUsers(ctx context.Context, opt fleet.UserListOptions) ([ return svc.ds.ListUsers(ctx, opt) } -//////////////////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////////////////// // Me (get own current user) -//////////////////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////////////////// +type getMeRequest struct { + IncludeUISettings bool `query:"include_ui_settings,optional"` +} func meEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (errorer, error) { user, err := svc.AuthenticatedUser(ctx) @@ -240,7 +243,15 @@ func meEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (er return getUserResponse{Err: err}, nil } } - return getUserResponse{User: user, AvailableTeams: availableTeams}, nil + req := request.(*getMeRequest) + var userSettings *fleet.UserSettings + if req.IncludeUISettings { + userSettings, err = svc.GetUserSettings(ctx, user.ID) + if err != nil { + return getUserResponse{Err: err}, nil + } + } + return getUserResponse{User: user, AvailableTeams: availableTeams, Settings: userSettings}, nil } func (svc *Service) AuthenticatedUser(ctx context.Context) (*fleet.User, error) { @@ -264,12 +275,14 @@ func (svc *Service) AuthenticatedUser(ctx context.Context) (*fleet.User, error) //////////////////////////////////////////////////////////////////////////////// type getUserRequest struct { - ID uint `url:"id"` + ID uint `url:"id"` + IncludeUISettings bool `query:"include_ui_settings,optional"` } type getUserResponse struct { User *fleet.User `json:"user,omitempty"` AvailableTeams []*fleet.TeamSummary `json:"available_teams"` + Settings *fleet.UserSettings `json:"settings,omitempty"` Err error `json:"error,omitempty"` } @@ -289,7 +302,22 @@ func getUserEndpoint(ctx context.Context, request interface{}, svc fleet.Service return getUserResponse{Err: err}, nil } } - return getUserResponse{User: user, AvailableTeams: availableTeams}, nil + + var userSettings *fleet.UserSettings + if req.IncludeUISettings { + userSettings, err = svc.GetUserSettings(ctx, user.ID) + if err != nil { + return getUserResponse{Err: err}, nil + } + } + return getUserResponse{User: user, AvailableTeams: availableTeams, Settings: userSettings}, nil +} + +func (svc *Service) GetUserSettings(ctx context.Context, userID uint) (*fleet.UserSettings, error) { + if err := svc.authz.Authorize(ctx, &fleet.User{ID: userID}, fleet.ActionRead); err != nil { + return nil, err + } + return svc.ds.UserSettings(ctx, userID) } // setAuthCheckedOnPreAuthErr can be used to set the authentication as checked @@ -455,6 +483,10 @@ func (svc *Service) ModifyUser(ctx context.Context, userID uint, p fleet.UserPay user.SSOEnabled = *p.SSOEnabled } + if p.Settings != nil { + user.Settings = p.Settings + } + currentUser := authz.UserFromContext(ctx) if p.GlobalRole != nil && *p.GlobalRole != "" { diff --git a/server/vulnerabilities/nvd/cpe_test.go b/server/vulnerabilities/nvd/cpe_test.go index 0628f95d67b5..7f10af535b2d 100644 --- a/server/vulnerabilities/nvd/cpe_test.go +++ b/server/vulnerabilities/nvd/cpe_test.go @@ -1314,6 +1314,16 @@ func TestCPEFromSoftwareIntegration(t *testing.T) { Version: "6.0.1", }, cpe: "", }, + { // checks vendor/product matching based on bundle name, including EAPs + software: fleet.Software{ + Name: "GoLand EAP.app", + Source: "apps", + Version: "2022.3.99.123.456", + Vendor: "", + BundleIdentifier: "com.jetbrains.goland-EAP", + }, + cpe: "cpe:2.3:a:jetbrains:goland:2022.3.99.123.456:*:*:*:*:macos:*:*", + }, { software: fleet.Software{ Name: "IntelliJ IDEA.app", diff --git a/server/vulnerabilities/nvd/cpe_translations.json b/server/vulnerabilities/nvd/cpe_translations.json index 59518162bdaa..d5a90b97a341 100644 --- a/server/vulnerabilities/nvd/cpe_translations.json +++ b/server/vulnerabilities/nvd/cpe_translations.json @@ -160,6 +160,116 @@ "vendor": ["jetbrains"] } }, + { + "software": { + "bundle_identifier": ["/^com\\.jetbrains\\.PhpStorm/"], + "source": ["apps"] + }, + "filter": { + "product": ["phpstorm"], + "vendor": ["jetbrains"] + } + }, + { + "software": { + "bundle_identifier": ["/^com\\.jetbrains\\.aqua/"], + "source": ["apps"] + }, + "filter": { + "product": ["aqua"], + "vendor": ["jetbrains"] + } + }, + { + "software": { + "bundle_identifier": ["/^com\\.jetbrains\\.CLion/"], + "source": ["apps"] + }, + "filter": { + "product": ["clion"], + "vendor": ["jetbrains"] + } + }, + { + "software": { + "bundle_identifier": ["/^com\\.jetbrains\\.datagrip/"], + "source": ["apps"] + }, + "filter": { + "product": ["datagrip"], + "vendor": ["jetbrains"] + } + }, + { + "software": { + "bundle_identifier": ["/^com\\.jetbrains\\.dataspell/"], + "source": ["apps"] + }, + "filter": { + "product": ["dataspell"], + "vendor": ["jetbrains"] + } + }, + { + "software": { + "bundle_identifier": ["/^com\\.jetbrains\\.goland/"], + "source": ["apps"] + }, + "filter": { + "product": ["goland"], + "vendor": ["jetbrains"] + } + }, + { + "software": { + "bundle_identifier": ["/^com\\.jetbrains\\.rider/"], + "source": ["apps"] + }, + "filter": { + "product": ["rider"], + "vendor": ["jetbrains"] + } + }, + { + "software": { + "bundle_identifier": ["/^com\\.jetbrains\\.rubymine/"], + "source": ["apps"] + }, + "filter": { + "product": ["rubymine"], + "vendor": ["jetbrains"] + } + }, + { + "software": { + "bundle_identifier": ["/^com\\.jetbrains\\.rustrover/"], + "source": ["apps"] + }, + "filter": { + "product": ["rustrover"], + "vendor": ["jetbrains"] + } + }, + { + "software": { + "bundle_identifier": ["/^com\\.jetbrains\\.WebStorm/"], + "source": ["apps"] + }, + "filter": { + "product": ["webstorm"], + "vendor": ["jetbrains"] + } + }, + { + "software": { + "bundle_identifier": ["/^com\\.jetbrains\\.mps/"], + "source": ["apps"] + }, + "filter": { + "product": ["mps"], + "vendor": ["jetbrains"] + } + }, { "software": { "name": ["ms-python.python"], diff --git a/server/vulnerabilities/nvd/cve_test.go b/server/vulnerabilities/nvd/cve_test.go index 22057fa7d8ed..60fe0547add4 100644 --- a/server/vulnerabilities/nvd/cve_test.go +++ b/server/vulnerabilities/nvd/cve_test.go @@ -367,6 +367,14 @@ func TestTranslateCPEToCVE(t *testing.T) { excludedCVEs: []string{"CVE-2024-10327"}, continuesToUpdate: true, }, + "cpe:2.3:a:jetbrains:goland:2022.3.99.123.456:*:*:*:*:macos:*:*": { + includedCVEs: []cve{{ID: "CVE-2024-37051", resolvedInVersion: ""}}, + continuesToUpdate: true, + }, + "cpe:2.3:a:jetbrains:goland:2024.3:*:*:*:*:macos:*:*": { + excludedCVEs: []string{"CVE-2024-37051"}, + continuesToUpdate: true, + }, } cveOSTests := []struct { diff --git a/terraform/addons/monitoring/main.tf b/terraform/addons/monitoring/main.tf index 57a3cb5ae3bd..7bd60c21af5e 100644 --- a/terraform/addons/monitoring/main.tf +++ b/terraform/addons/monitoring/main.tf @@ -448,7 +448,10 @@ data "aws_iam_policy_document" "cron_monitoring_lambda" { "sns:Publish" ] - resources = lookup(var.sns_topic_arns_map, "cron_monitoring", var.default_sns_topic_arns) + resources = distinct(concat( + lookup(var.sns_topic_arns_map, "cron_monitoring", var.default_sns_topic_arns), + lookup(var.sns_topic_arns_map, "cron_job_failure_monitoring", var.default_sns_topic_arns) + )) effect = "Allow" } diff --git a/website/api/controllers/admin/get-llm-generated-sql.js b/website/api/controllers/admin/get-llm-generated-sql.js new file mode 100644 index 000000000000..7a56810edf38 --- /dev/null +++ b/website/api/controllers/admin/get-llm-generated-sql.js @@ -0,0 +1,104 @@ +module.exports = { + + + friendlyName: 'Get llm generated sql', + + + description: '', + + + + inputs: { + naturalLanguageQuestion: { type: 'string', required: true } + }, + + + exits: { + success: { + description: 'A SQL query was generated' + }, + + errorFromOpenAi: { + description: 'The Open AI API reutrned an error.' + } + }, + + + fn: async function ({naturalLanguageQuestion}) { + + let completeTables = await sails.helpers.getExtendedOsquerySchema(); + let prunedTables = completeTables.map((table)=>{ + let newTable = _.pick(table,['name','description','platforms', 'examples']); + newTable.columns = table.columns.map((column) => _.pick(column, ['name', 'description', 'type', 'platforms', 'required'])); + return newTable; + }); + + // Filter down the schema. + let schemaFiltrationPrompt = `Given this question from an IT admin, and using the provided context (the osquery schema), return the subset of tables that might be relevant for designing an osquery SQL query to answer this question for computers running macOS, Windows, Linux, and/or ChromeOS. + + Here is the question: + \`\`\` + ${naturalLanguageQuestion} + \`\`\` + + Provided context: + \`\`\` + ${JSON.stringify(prunedTables.map((table)=>{ + let lighterTable = _.pick(table, ['name','description','platforms']); + lighterTable.columns = table.columns.map((column)=>{ + let lighterColumn = _.pick(column, ['name', 'description', 'platforms']); + return lighterColumn; + }); + return lighterTable;}))} + \`\`\` + + Please respond in JSON, with the same data shape as the provided context, but with the array filtered to include only relevant tables.`; + let filteredTables = await sails.helpers.ai.prompt(schemaFiltrationPrompt, 'gpt-4o', true) + .intercept((err)=>{ + return new Error(`When trying to get a subset of tables to use to generate a query for an Admin user, the Open AI API returned an error. Full error: ${require('util').inspect(err, {depth: 2})}`); + }); + + + + // Now generate the SQL. + let sqlPrompt = `Given this question from an IT admin, return osquery SQL I could run on a computer (or fleet of computers) to answer this question. + + Here is the question: + \`\`\` + ${naturalLanguageQuestion} + \`\`\` + + When generating the SQL: + 1. Please do not use the SQL "AS" operator, nor alias tables. Always reference tables by their full name. + 2. If this question is related to an application or program, consider using LIKE instead of something verbatim. + 3. If this question is not possible to ask given the tables and columns available in the provided context (the osquery schema) for a particular operating system, then use empty string. + 4. If this question is a "yes" or "no" question, or a "how many people" question, or a "how many hosts" question, then build the query such that a "yes" returns exactly one row and a "no" returns zero rows. In other words, if this question is about finding out which hosts match a "yes" or "no" question, then if a host does not match, do not include any rows for it. + 5. Use only tables that are supported for each target platform, as documented in the provided context, considering the examples if they exist, and the available columns. + 6. For each table that you use, only use columns that are documented for that table, as documented in the provided context. + + Provided context: + \`\`\` + ${JSON.stringify(filteredTables)} + \`\`\` + + Please give me all of the above in JSON, with this data shape: + + { + "macOSQuery": "TODO", + "windowsQuery": "TODO", + "linuxQuery": "TODO", + "chromeOSQuery": "TODO", + "macOSCaveats": "TODO", + "windowsCaveats": "TODO", + "linuxCaveats": "TODO", + "chromeOSCaveats": "TODO", + }`; + let sqlReport = await sails.helpers.ai.prompt(sqlPrompt, 'o1-preview', true) + .intercept((err)=>{ + return new Error(`When trying to generate a query for an Admin user, the Open AI API returned an error. Full error: ${require('util').inspect(err, {depth: 2})}`); + }); + return sqlReport; + } + + +}; diff --git a/website/api/controllers/admin/view-query-generator.js b/website/api/controllers/admin/view-query-generator.js new file mode 100644 index 000000000000..db36affb4b8c --- /dev/null +++ b/website/api/controllers/admin/view-query-generator.js @@ -0,0 +1,27 @@ +module.exports = { + + + friendlyName: 'View query generator', + + + description: 'Display "Query generator" page.', + + + exits: { + + success: { + viewTemplatePath: 'pages/admin/query-generator' + } + + }, + + + fn: async function () { + + // Respond with view. + return {}; + + } + + +}; diff --git a/website/assets/js/cloud.setup.js b/website/assets/js/cloud.setup.js index 35f7bcdf45de..a8c3aaac405a 100644 --- a/website/assets/js/cloud.setup.js +++ b/website/assets/js/cloud.setup.js @@ -13,7 +13,7 @@ Cloud.setup({ /* eslint-disable */ - methods: {"redirectToStripeBillingPortal":{"verb":"GET","url":"/customers/update-subscription","args":[]},"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","numSoftwareVersions","numHostSoftwares","numSoftwareTitles","numHostSoftwareInstalledPaths","numSoftwareCPEs","numSoftwareCVEs","aiFeaturesDisabled","maintenanceWindowsEnabled","maintenanceWindowsConfigured","numHostsFleetDesktopEnabled"]},"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"]},"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","redirectToExternalPageAfterAuthorization","sharedSecret"]},"redirectVantaAuthorizationRequest":{"verb":"GET","url":"/redirect-vanta-authorization-request","args":["vantaSourceId","state","vantaAuthorizationRequestURL","redirectAfterSetup"]},"deliverMdmBetaSignup":{"verb":"POST","url":"/api/v1/deliver-mdm-beta-signup","args":["emailAddress","fullName","jobTitle","numberOfHosts"]},"getHumanInterpretationFromOsquerySql":{"verb":"POST","url":"/api/v1/get-human-interpretation-from-osquery-sql","args":["sql"]},"deliverAppleCsr":{"verb":"POST","url":"/api/v1/deliver-apple-csr","args":["unsignedCsrData","deliveryMethod"]},"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"]},"updateStartCtaVisibility":{"verb":"POST","url":"/api/v1/account/update-start-cta-visibility","args":[]},"deliverDealRegistrationSubmission":{"verb":"POST","url":"/api/v1/deliver-deal-registration-submission","args":["submittersFirstName","submittersLastName","submittersEmailAddress","submittersOrganization","customersFirstName","customersLastName","customersEmailAddress","linkedinUrl","customersOrganization","customersCurrentMdm","otherMdmEvaluated","preferredHosting","expectedDealSize","expectedCloseDate","notes"]},"unsubscribeFromMarketingEmails":{"verb":"GET","url":"/api/v1/unsubscribe-from-marketing-emails","args":["emailAddress"]},"getStripeCheckoutSessionUrl":{"verb":"POST","url":"/api/v1/customers/get-stripe-checkout-session-url","args":["quoteId"]}} + methods: {"redirectToStripeBillingPortal":{"verb":"GET","url":"/customers/update-subscription","args":[]},"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","numSoftwareVersions","numHostSoftwares","numSoftwareTitles","numHostSoftwareInstalledPaths","numSoftwareCPEs","numSoftwareCVEs","aiFeaturesDisabled","maintenanceWindowsEnabled","maintenanceWindowsConfigured","numHostsFleetDesktopEnabled","numQueries"]},"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"]},"getEstDeviceCertificate":{"verb":"POST","url":"/api/v1/get-est-device-certificate","args":["csrData","authToken"]},"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"]},"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","redirectToExternalPageAfterAuthorization","sharedSecret"]},"redirectVantaAuthorizationRequest":{"verb":"GET","url":"/redirect-vanta-authorization-request","args":["vantaSourceId","state","vantaAuthorizationRequestURL","redirectAfterSetup"]},"deliverMdmBetaSignup":{"verb":"POST","url":"/api/v1/deliver-mdm-beta-signup","args":["emailAddress","fullName","jobTitle","numberOfHosts"]},"getHumanInterpretationFromOsquerySql":{"verb":"POST","url":"/api/v1/get-human-interpretation-from-osquery-sql","args":["sql"]},"deliverAppleCsr":{"verb":"POST","url":"/api/v1/deliver-apple-csr","args":["unsignedCsrData","deliveryMethod"]},"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"]},"updateStartCtaVisibility":{"verb":"POST","url":"/api/v1/account/update-start-cta-visibility","args":[]},"deliverDealRegistrationSubmission":{"verb":"POST","url":"/api/v1/deliver-deal-registration-submission","args":["submittersFirstName","submittersLastName","submittersEmailAddress","submittersOrganization","customersFirstName","customersLastName","customersEmailAddress","linkedinUrl","customersOrganization","customersCurrentMdm","otherMdmEvaluated","preferredHosting","expectedDealSize","expectedCloseDate","notes"]},"unsubscribeFromMarketingEmails":{"verb":"GET","url":"/api/v1/unsubscribe-from-marketing-emails","args":["emailAddress"]},"getStripeCheckoutSessionUrl":{"verb":"POST","url":"/api/v1/customers/get-stripe-checkout-session-url","args":["quoteId"]},"getLlmGeneratedSql":{"verb":"POST","url":"/api/v1/admin/get-llm-generated-sql","args":["naturalLanguageQuestion"]}} /* eslint-enable */ }); diff --git a/website/assets/js/pages/admin/query-generator.page.js b/website/assets/js/pages/admin/query-generator.page.js new file mode 100644 index 000000000000..319e923ab80e --- /dev/null +++ b/website/assets/js/pages/admin/query-generator.page.js @@ -0,0 +1,56 @@ +parasails.registerPage('query-generator', { + // ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗ + // β•‘β•‘β•‘β•‘β•‘ β•‘ ║╠═╣║ β•šβ•β•— β•‘ ╠═╣ β•‘ β•‘β•£ + // β•©β•β•šβ•β•© β•© β•©β•© ╩╩═╝ β•šβ•β• β•© β•© β•© β•© β•šβ•β• + 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: { + naturalLanguageQuestion: {required: true} + }, + // Syncing / loading state + syncing: false, + queryResult: '', + // Server error state + cloudError: '', + showGeneratedQuery: false, + }, + + // ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗ + // β•‘ β•‘β• β•£ β•‘β•£ β•‘ β•šβ•¦β•β•‘ β•‘ β•‘β•£ + // β•©β•β•β•©β•š β•šβ•β•β•šβ•β• β•© β•šβ•β•β•©β•β•β•šβ•β• + beforeMount: function() { + //… + }, + mounted: async function() { + //… + }, + + // ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗ + // β•‘β•‘β•‘β•‘ β•‘ β•‘β•£ ╠╦╝╠═╣║ β•‘ β•‘β•‘ β•‘β•‘β•‘β•‘β•šβ•β•— + // β•©β•β•šβ• β•© β•šβ•β•β•©β•šβ•β•© β•©β•šβ•β• β•© β•©β•šβ•β•β•β•šβ•β•šβ•β• + methods: { + handleSubmittingForm: async function() { + let argins = this.formData.naturalLanguageQuestion; + this.queryResult = await Cloud.getLlmGeneratedSql(argins) + .tolerate((err)=>{ + this.cloudError = err; + this.syncing = false; + }); + }, + submittedQueryForm: function() { + if(!this.cloudError){ + this.showGeneratedQuery = true; + } + }, + clickResetQueryGenerator: function() { + this.showGeneratedQuery = false; + this.formData.naturalLanguageQuestion = ''; + } + } +}); diff --git a/website/assets/styles/importer.less b/website/assets/styles/importer.less index 9ccdabe895b5..b44c9b3303d8 100644 --- a/website/assets/styles/importer.less +++ b/website/assets/styles/importer.less @@ -81,4 +81,4 @@ @import 'pages/app-library.less'; @import 'pages/app-details.less'; @import 'pages/meetups.less'; - +@import 'pages/admin/query-generator.less'; diff --git a/website/assets/styles/pages/admin/query-generator.less b/website/assets/styles/pages/admin/query-generator.less new file mode 100644 index 000000000000..d2a129edb767 --- /dev/null +++ b/website/assets/styles/pages/admin/query-generator.less @@ -0,0 +1,11 @@ +#query-generator { + + [purpose='page-container'] { + padding: 64px; + } + [purpose='page-content'] { + max-width: 1072px; + margin: auto; + } + +} diff --git a/website/assets/styles/pages/articles/articles.less b/website/assets/styles/pages/articles/articles.less index 7d5fd9f09742..7971d4acc16a 100644 --- a/website/assets/styles/pages/articles/articles.less +++ b/website/assets/styles/pages/articles/articles.less @@ -32,6 +32,10 @@ } [purpose='category-title'] { padding-bottom: 40px; + margin-right: 25px; + p { + margin-bottom: 0px; + } } [purpose='guides-category-page'] { @@ -40,6 +44,36 @@ margin-right: 6px; } } + [purpose='changelog-button'] { + display: flex; + padding: var(--spacing-spacing-xxs, 4px) var(--spacing-spacing-sm, 16px); + justify-content: center; + align-items: center; + gap: var(--spacing-spacing-xxs, 4px); + border-radius: var(--spacing-spacing-xs, 8px); + border: 1px solid var(--color-grey-200, #E2E4EA); + background: var(--color-grey-50, #F9FAFC); + color: #515774; + text-align: center; + font-family: Inter; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 21px; + width: 168px; + img { + width: 16px; + height: 16px; + margin-right: 8px; + } + &:hover { + color: #515774; + text-decoration: none; + border-radius: var(--spacing-spacing-xs, 8px); + border: 1px solid var(--color-grey-400, #C5C7D1); + background: var(--color-grey-100, #F2F2F5); + } + } [purpose='rss-button'] { padding: 4px 8px; cursor: pointer; @@ -290,6 +324,10 @@ margin-left: 10px; margin-right: 10px; } + [purpose='category-title'] { + padding-bottom: 40px; + margin-right: 35px; + } [purpose='articles'] { [purpose='article-card'] { flex: 1 1 350px; @@ -304,6 +342,7 @@ } @media (min-width: 991px) { + [purpose='articles'] { [purpose='article-card'] { flex: 1 1 290px; @@ -316,6 +355,9 @@ [purpose='page-container'] { padding: 64px 32px; } + [purpose='category-title'] { + margin-right: 20px; + } [purpose='guide-card'] { max-width: unset; } @@ -358,6 +400,11 @@ width: 100%; } } + [purpose='category-title'] { + p { + margin-bottom: 16px; + } + } [purpose='guides'] { column-count: 2; } @@ -392,6 +439,7 @@ max-width: 100%; } } + [purpose='guide-card'] { width: 100%; } diff --git a/website/assets/styles/pages/query-detail.less b/website/assets/styles/pages/query-detail.less index 8026d382f41b..3e56e7d5add3 100644 --- a/website/assets/styles/pages/query-detail.less +++ b/website/assets/styles/pages/query-detail.less @@ -155,6 +155,33 @@ line-height: 150%; margin-bottom: 0px; padding: 16px 0px 32px 0px; + } + [purpose='policy-attribution'] { + display: flex; + flex-direction: row; + align-items: center; + padding-top: 8px; + padding-bottom: 16px; + [purpose='contributor-profile-picture'] { + width: 28.8px; + height: 28.8px; + margin-right: 8px; + border-radius: 50%; + } + [purpose='contributor-profile-name'] { + font-size: 14px; + line-height: 150%; + } + [purpose='policy-link'] { + color: unset; + text-decoration: none; + + &:hover { + text-decoration: none; + } + } + + } [purpose='policy-details'] { padding-right: 64px; diff --git a/website/assets/styles/pages/transparency.less b/website/assets/styles/pages/transparency.less index 68d561ac880f..70daec8c8ef5 100644 --- a/website/assets/styles/pages/transparency.less +++ b/website/assets/styles/pages/transparency.less @@ -212,6 +212,13 @@ } + [purpose='agents-note'] { + color: #515774; + font-size: 16px; + line-height: 24px; + padding-top: 24px; + margin-bottom: 40px; + } [purpose='accordion'] { @@ -221,7 +228,6 @@ [purpose='accordion-item'] { border-bottom: 1px solid #E2E4EA; - padding-bottom: ; margin-top: 16px; margin-bottom: 16px; } @@ -295,6 +301,7 @@ text-align: center; margin-bottom: 0px; } + } @media (max-width: 767px) { @@ -303,7 +310,6 @@ padding-top: 40px; padding-bottom: 40px; } - [purpose='feature-row'] { flex-direction: row; align-items: start; diff --git a/website/config/routes.js b/website/config/routes.js index 6293d097c4e3..2e8ef0c80487 100644 --- a/website/config/routes.js +++ b/website/config/routes.js @@ -448,6 +448,8 @@ module.exports.routes = { } }, + 'GET /admin/query-generator': { action: 'admin/view-query-generator' }, + // ╦ ╔═╗╔═╗╔═╗╔═╗╦ ╦ ╦═╗╔═╗╔╦╗╦╦═╗╔═╗╔═╗╔╦╗╔═╗ // β•‘ β•‘β•£ β•‘ ╦╠═╣║ β•šβ•¦β• ╠╦╝║╣ ║║║╠╦╝║╣ β•‘ β•‘ β•šβ•β•— // β•©β•β•β•šβ•β•β•šβ•β•β•© β•©β•šβ•β• β•© β•©β•šβ•β•šβ•β•β•β•©β•β•©β•©β•šβ•β•šβ•β•β•šβ•β• β•© β•šβ•β• @@ -905,4 +907,5 @@ module.exports.routes = { 'POST /api/v1/deliver-deal-registration-submission': { action: 'deliver-deal-registration-submission' }, '/api/v1/unsubscribe-from-marketing-emails': { action: 'unsubscribe-from-marketing-emails' }, 'POST /api/v1/customers/get-stripe-checkout-session-url': { action: 'customers/get-stripe-checkout-session-url' }, + 'POST /api/v1/admin/get-llm-generated-sql': { action: 'admin/get-llm-generated-sql' }, }; diff --git a/website/package.json b/website/package.json index d6029c1187a5..24fcd9199334 100644 --- a/website/package.json +++ b/website/package.json @@ -18,7 +18,8 @@ "sails-hook-orm": "^4.0.3", "sails-hook-sockets": "^3.0.0", "sails-postgresql": "^5.0.1", - "stripe": "17.3.1" + "stripe": "17.3.1", + "yaml": "1.10.2" }, "devDependencies": { "eslint": "5.16.0", @@ -26,8 +27,7 @@ "htmlhint": "0.11.0", "lesshint": "6.3.6", "marked": "4.0.10", - "sails-hook-grunt": "^5.0.0", - "yaml": "1.10.2" + "sails-hook-grunt": "^5.0.0" }, "scripts": { "custom-tests": "echo \"(No other custom tests yet.)\" && echo", diff --git a/website/views/layouts/layout.ejs b/website/views/layouts/layout.ejs index 1c2409f267d8..de22654b3b4a 100644 --- a/website/views/layouts/layout.ejs +++ b/website/views/layouts/layout.ejs @@ -187,6 +187,7 @@ What people are saying News Ask around + Meetups COMPANY
Origins   (Fleet & osquery) @@ -249,6 +250,7 @@ News Ask around Take a tour + Meetups COMPANY Origins   (Fleet & osquery) The handbook @@ -287,6 +289,7 @@ Admin pages
+ Generate queries License generator HTML Email preview tool
@@ -468,6 +471,7 @@ + diff --git a/website/views/pages/admin/query-generator.ejs b/website/views/pages/admin/query-generator.ejs new file mode 100644 index 000000000000..9cab588f4db2 --- /dev/null +++ b/website/views/pages/admin/query-generator.ejs @@ -0,0 +1,25 @@ +
+
+
+ + +

Query generator

+
+ + +
Ask your question.
+
+ An error occurred while generating your queries. Please reload this page and try again. + Generate queries +
+ +
+

Generated Query:

+
{{queryResult}}
+
Generate another
+
+ +
+
+
+<%- /* Expose server-rendered data as window.SAILS_LOCALS :: */ exposeLocalsToBrowser() %> diff --git a/website/views/pages/articles/articles.ejs b/website/views/pages/articles/articles.ejs index f3cf3e267715..2c77fb33c120 100644 --- a/website/views/pages/articles/articles.ejs +++ b/website/views/pages/articles/articles.ejs @@ -22,6 +22,9 @@ +
+ GitHub logoView changelog +
diff --git a/website/views/pages/meetups.ejs b/website/views/pages/meetups.ejs index 953c585d8838..2267285a4fb8 100644 --- a/website/views/pages/meetups.ejs +++ b/website/views/pages/meetups.ejs @@ -31,7 +31,7 @@

This group is intended for anyone who supports or manages a group of Macs or iOS devices of any size (from less than a dozen to ten of thousands) for an institution of any stripe (Small/Medium Business, Non-profit, Enterprise, Education) in the Greater Philadelphia area (South Eastern PA, South Jersey, Delaware) to swap ideas and solutions, network, and hang out.

- View details + View details
diff --git a/website/views/pages/observability.ejs b/website/views/pages/observability.ejs index e5318882e123..4d867d4df041 100644 --- a/website/views/pages/observability.ejs +++ b/website/views/pages/observability.ejs @@ -234,14 +234,14 @@
Attack surface management -
Attack surface management
-

Discover security misconfigurations and vulnerabilities and prioritize risks that matter to your organization.

+
Security posture
+

Identify security misconfigurations and vulnerabilities and prioritize risks that matter to your organization.

Malware detection -
Malware detection
-

Continuously scan host filesystems for indicators of compromise (IOC). Import malware signatures from threat intelligence sources.

+
Threat management
+

Use attack signatures from threat intelligence sources to scan and resolve indicators of compromise (IOC).

diff --git a/website/views/pages/query-detail.ejs b/website/views/pages/query-detail.ejs index d4cccec5bb19..86d3d053d8e0 100644 --- a/website/views/pages/query-detail.ejs +++ b/website/views/pages/query-detail.ejs @@ -30,6 +30,11 @@

<%- query.name %>

+
+ Contributor's GitHub profile picture + +

<%= query.contributors[0].name %>

+

<%- query.description %>