diff --git a/changes/26405-jetbrains b/changes/26405-jetbrains new file mode 100644 index 0000000000..524b2e89c3 --- /dev/null +++ b/changes/26405-jetbrains @@ -0,0 +1 @@ +- fixed issue where Windows Jetbrains products would not report the correct version number \ No newline at end of file diff --git a/docs/Contributing/product-groups/orchestration/understanding-host-vitals.md b/docs/Contributing/product-groups/orchestration/understanding-host-vitals.md index 17641e86c4..ee768068c4 100644 --- a/docs/Contributing/product-groups/orchestration/understanding-host-vitals.md +++ b/docs/Contributing/product-groups/orchestration/understanding-host-vitals.md @@ -1157,6 +1157,46 @@ FROM chocolatey_packages SELECT 1 FROM registry WHERE key = 'HKEY_LOCAL_MACHINE\SOFTWARE\Adobe\Adobe Acrobat\DC' ``` +## software_windows_jetbrains + +- Description: A software override query to use the version from the product-info.json file for JetBrains programs on Windows. + +- Platforms: windows + +- Discovery query: +```sql +SELECT 1 FROM osquery_registry WHERE active = true AND registry = 'table' AND name = 'file_contents' +``` + +- Query: +```sql +SELECT + p.name AS name, + + COALESCE( + trim(json_extract(fc.contents, '$.version'), '"'), + p.version + ) AS version, + + '' AS extension_id, + '' AS extension_for, + 'programs' AS source, + p.publisher AS vendor, + p.install_location AS installed_path, + p.upgrade_code AS upgrade_code + + FROM programs p + LEFT JOIN file_contents fc + ON fc.path = CASE + WHEN p.install_location IS NULL OR p.install_location = '' + THEN NULL + ELSE rtrim(p.install_location, '\') || '\product-info.json' + END + + WHERE p.publisher LIKE '%JetBrains%' + AND p.name NOT LIKE '%Toolbox%' +``` + ## software_windows_last_opened_at - Description: A software override query[^1] to append last_opened_at information to Windows software entries. diff --git a/server/service/osquery_test.go b/server/service/osquery_test.go index 687a6e92f1..dd6590852a 100644 --- a/server/service/osquery_test.go +++ b/server/service/osquery_test.go @@ -1208,6 +1208,7 @@ func verifyDiscovery(t *testing.T, queries, discovery map[string]string) { hostDetailQueryPrefix + "software_macos_executable_sha256": {}, hostDetailQueryPrefix + "software_rpm_last_opened_at": {}, hostDetailQueryPrefix + "software_deb_last_opened_at": {}, + hostDetailQueryPrefix + "software_windows_jetbrains": {}, } for name := range queries { require.NotEmpty(t, discovery[name]) diff --git a/server/service/osquery_utils/queries.go b/server/service/osquery_utils/queries.go index c0b983f359..8f05c0c461 100644 --- a/server/service/osquery_utils/queries.go +++ b/server/service/osquery_utils/queries.go @@ -1344,6 +1344,44 @@ FROM chrome_extensions`, // Software queries expect specific columns to be present. Reference the // software_{macos|windows|linux} queries for the expected columns. var SoftwareOverrideQueries = map[string]DetailQuery{ + // windows_jetbrains uses the version contained in the product-info.json file as exe installers + // provide an unconvertible build number in the programs table not used in vulnerability matching. + "windows_jetbrains": { + Description: "A software override query to use the version from the product-info.json file for JetBrains programs on Windows.", + Query: ` + SELECT + p.name AS name, + + COALESCE( + trim(json_extract(fc.contents, '$.version'), '"'), + p.version + ) AS version, + + '' AS extension_id, + '' AS extension_for, + 'programs' AS source, + p.publisher AS vendor, + p.install_location AS installed_path, + p.upgrade_code AS upgrade_code + + FROM programs p + LEFT JOIN file_contents fc + ON fc.path = CASE + WHEN p.install_location IS NULL OR p.install_location = '' + THEN NULL + ELSE rtrim(p.install_location, '\') || '\product-info.json' + END + + WHERE p.publisher LIKE '%JetBrains%' + AND p.name NOT LIKE '%Toolbox%' +`, + Platforms: []string{"windows"}, + DirectIngestFunc: directIngestSoftware, + Discovery: discoveryTable("file_contents"), + SoftwareOverrideMatch: func(row map[string]string) bool { + return strings.Contains(row["vendor"], "JetBrains") && !strings.Contains(row["name"], "Toolbox") + }, + }, // windows_acrobat_dc checks the Windows registry to determine if "DC" should be appended to the Adobe Acrobat // product name. While Adobe recently rebranded the free version to "Adobe Acrobat (64-bit)" — matching // the naming convention of the paid product — our vulnerability detection engine requires the "DC" postfix for accurate diff --git a/server/service/osquery_utils/queries_test.go b/server/service/osquery_utils/queries_test.go index c5837e6394..2fcb0d9536 100644 --- a/server/service/osquery_utils/queries_test.go +++ b/server/service/osquery_utils/queries_test.go @@ -416,7 +416,7 @@ func TestGetDetailQueries(t *testing.T) { queriesWithUsersAndSoftware := GetDetailQueries(t.Context(), config.FleetConfig{App: config.AppConfig{EnableScheduledQueryStats: true}}, nil, &fleet.Features{EnableHostUsers: true, EnableSoftwareInventory: true}, Integrations{}, nil) qs = baseQueries qs = append(qs, "users", "users_chrome", "software_macos", "software_linux", "software_windows", "software_vscode_extensions", "software_jetbrains_plugins", "software_linux_fleetd_pacman", - "software_chrome", "software_python_packages", "software_python_packages_with_users_dir", "scheduled_query_stats", "software_macos_firefox", "software_macos_codesign", "software_macos_executable_sha256", "software_windows_last_opened_at", "software_deb_last_opened_at", "software_rpm_last_opened_at", "software_windows_acrobat_dc") + "software_chrome", "software_python_packages", "software_python_packages_with_users_dir", "scheduled_query_stats", "software_macos_firefox", "software_macos_codesign", "software_macos_executable_sha256", "software_windows_last_opened_at", "software_deb_last_opened_at", "software_rpm_last_opened_at", "software_windows_acrobat_dc", "software_windows_jetbrains") require.Len(t, queriesWithUsersAndSoftware, len(qs)) sortedKeysCompare(t, queriesWithUsersAndSoftware, qs)