Closes #45206 ## Summary - Adds a new `adobe_plugins` osquery extension table to fleetd (macOS + Windows) - Parses CEP (`CSXS/manifest.xml`) and UXP (`manifest.json`) manifests for rich metadata - Falls back to filesystem info for native plug-ins where no manifest exists - Supports a `scan_level` WHERE constraint: `standard` (default) or `deep` ## Table schema | Column | Type | Description | | --- | --- | --- | | `path` | TEXT | Full path to the plugin directory or file | | `name` | TEXT | Plugin display name (from manifest or directory name) | | `version` | TEXT | Plugin version (from manifest) | | `vendor` | TEXT | Plugin author/publisher (from manifest) | | `bundle_id` | TEXT | Plugin bundle identifier (from manifest) | | `host_application` | TEXT | Target app(s): Photoshop, Illustrator, Premiere Pro, etc. | | `extension_type` | TEXT | `CEP`, `UXP`, or `native` | | `user` | TEXT | Username for user-scoped installs; empty for system-wide | | `platform` | TEXT | `darwin` or `windows` | | `scan_level` | TEXT | WHERE constraint only — `standard` (default) or `deep` | ## How I tested it > **Note:** Manual testing was done by installing two real open-source CEP extensions (downloaded from GitHub) on a macOS host without a full Adobe CC installation. This validates the table logic, manifest parsing, and osquery integration end-to-end against real-world manifest formats. **QA should test against machines with full Adobe Creative Cloud installations** (Photoshop, Premiere, Illustrator, etc.) to verify the scan paths match what Adobe actually ships, and to exercise `scan_level = 'deep'` with real native plug-in directories. Expect a few more dev cycles after QA feedback. ### 1. Unit tests — 22 passing ``` $ go test ./orbit/pkg/table/adobe_plugins/... -v --- PASS: TestParseCEPPlugin/valid_manifest --- PASS: TestParseCEPPlugin/missing_manifest_falls_back_to_dir_name --- PASS: TestParseCEPPlugin/malformed_manifest_falls_back_to_dir_name --- PASS: TestParseUXPPlugin/valid_manifest --- PASS: TestParseUXPPlugin/missing_manifest_falls_back_to_dir_name --- PASS: TestParseUXPPlugin/manifest_with_id_but_no_name_uses_id --- PASS: TestParseNativePlugin/* (5 subtests) --- PASS: TestResolveHostApps/* (7 subtests) --- PASS: TestScanEntry/* (2 subtests) PASS ``` ### 2. Cross-platform compilation ``` $ go build ./orbit/pkg/table/adobe_plugins/... # macOS ✅ $ GOOS=windows go build ./orbit/pkg/table/adobe_plugins/... # Windows ✅ $ GOOS=linux go build ./orbit/pkg/table/adobe_plugins/... # Linux stub ✅ $ go build ./orbit/cmd/fleetd_tables/ # Full fleetd binary ✅ $ go vet ./orbit/pkg/table/adobe_plugins/... # Clean ✅ ``` ### 3. Manual end-to-end testing on macOS (osquery 5.23.0) #### Setup Built the fleetd extension binary, then installed two **real open-source CEP extensions** from GitHub into the user-scoped scan path (`~/Library/Application Support/Adobe/CEP/extensions/`): 1. **[adobe-discord-rpc](https://github.com/Kuredew/adobe-discord-rpc)** — a real CEP extension targeting 11 Adobe apps. Has no `<Author>` element (tests missing-vendor edge case). Complex manifest with many host app codes. 2. **[cep-template](https://github.com/khanyuinc/cep-template)** — a CEP starter template targeting After Effects only. Minimal manifest. ```bash # Build extension go build -o build/fleetd-tables-test ./orbit/cmd/fleetd_tables/ # Install real extensions CEP_DIR="$HOME/Library/Application Support/Adobe/CEP/extensions" mkdir -p "$CEP_DIR/adobe-discord-rpc/CSXS" # downloaded CSXS/manifest.xml from GitHub into the directory mkdir -p "$CEP_DIR/cep-template/CSXS" # downloaded CSXS/manifest.xml from GitHub into the directory ``` #### Running the query ```bash OSQUERYD="/opt/orbit/bin/osqueryd/macos-app/stable/osquery.app/Contents/MacOS/osqueryd" $OSQUERYD -S --allow_unsafe --extensions_timeout=10 \ --extensions_require=com.fleetdm.fleetd_tables.osquery_extension.v1 \ --extension build/fleetd-tables-test \ --json "SELECT * FROM adobe_plugins;" ``` #### Actual output (verbatim) ```json [ { "bundle_id": "com.kureichi.discordrpc", "extension_type": "CEP", "host_application": "After Effects, Photoshop, Premiere Pro, InCopy, Audition, Dreamweaver, Animate, InDesign, Illustrator, Prelude", "name": "adobe-discord-rpc", "path": "/Users/sharonkatz/Library/Application Support/Adobe/CEP/extensions/adobe-discord-rpc", "platform": "darwin", "scan_level": "", "user": "sharonkatz", "vendor": "", "version": "3.1.1" }, { "bundle_id": "com.yourcompany", "extension_type": "CEP", "host_application": "After Effects", "name": "cep-template", "path": "/Users/sharonkatz/Library/Application Support/Adobe/CEP/extensions/cep-template", "platform": "darwin", "scan_level": "", "user": "sharonkatz", "vendor": "", "version": "1.0" } ] ``` #### osqueryi table output ``` +-------------------+---------+-------------------------+----------------------------------------------------------------------------------------------------------------+----------------+------------+ | name | version | bundle_id | host_application | extension_type | user | +-------------------+---------+-------------------------+----------------------------------------------------------------------------------------------------------------+----------------+------------+ | adobe-discord-rpc | 3.1.1 | com.kureichi.discordrpc | After Effects, Photoshop, Premiere Pro, InCopy, Audition, Dreamweaver, Animate, InDesign, Illustrator, Prelude | CEP | sharonkatz | | cep-template | 1.0 | com.yourcompany | After Effects | CEP | sharonkatz | +-------------------+---------+-------------------------+----------------------------------------------------------------------------------------------------------------+----------------+------------+ ``` #### What this verified | Scenario | Result | | --- | --- | | Real CEP manifest with 11 host apps | ✅ All codes resolved (AEFT→After Effects, PHSP/PHXS→Photoshop, PPRO→Premiere Pro, etc.) | | Missing `<Author>` element | ✅ `vendor` is empty string, no crash | | Minimal CEP manifest (single host) | ✅ `host_application=After Effects`, version/bundle_id correct | | User-scoped detection | ✅ `user=sharonkatz` populated | | Schema registration | ✅ `.schema adobe_plugins` shows all 10 columns | | No Adobe installed + no plugins | ✅ 0 rows, no error | | Deep scan with no app bundles | ✅ 0 extra rows, no error | ### Windows Not tested yet — Windows paths are implemented and cross-compile, but need manual verification on a Windows host with Adobe CC. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **New Features** * Added Adobe plugins osquery table for macOS and Windows platforms * Discovers and catalogs Adobe CEP, UXP, and native plugins * Extracts plugin metadata including version, vendor, host applications, and installation paths * Supports configurable scan depth for comprehensive plugin discovery <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/fleetdm/fleet/pull/45208) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
61 lines
1.6 KiB
Go
61 lines
1.6 KiB
Go
//go:build windows
|
|
|
|
package table
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/fleetdm/fleet/v4/orbit/pkg/table/adobe_plugins"
|
|
"github.com/fleetdm/fleet/v4/orbit/pkg/table/bitlocker_key_protectors"
|
|
cisaudit "github.com/fleetdm/fleet/v4/orbit/pkg/table/cis_audit"
|
|
"github.com/fleetdm/fleet/v4/orbit/pkg/table/mdm_bridge"
|
|
"github.com/fleetdm/fleet/v4/orbit/pkg/table/windowsupdatetable"
|
|
"github.com/rs/zerolog/log"
|
|
"golang.org/x/sys/windows/registry"
|
|
|
|
"github.com/osquery/osquery-go"
|
|
"github.com/osquery/osquery-go/plugin/table"
|
|
)
|
|
|
|
func PlatformTables(_ PluginOpts) ([]osquery.OsqueryPlugin, error) {
|
|
plugins := []osquery.OsqueryPlugin{
|
|
// Fleet tables
|
|
adobe_plugins.TablePlugin(log.Logger),
|
|
table.NewPlugin("cis_audit", cisaudit.Columns(), cisaudit.Generate),
|
|
|
|
bitlocker_key_protectors.TablePlugin(log.Logger),
|
|
|
|
windowsupdatetable.TablePlugin(windowsupdatetable.UpdatesTable, log.Logger), // table name is "windows_updates"
|
|
}
|
|
|
|
windowsServer, err := IsWindowsServer()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !windowsServer {
|
|
plugins = append(plugins, table.NewPlugin("mdm_bridge", mdm_bridge.Columns(), mdm_bridge.Generate))
|
|
}
|
|
|
|
return plugins, nil
|
|
}
|
|
|
|
func IsWindowsServer() (bool, error) {
|
|
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
|
|
if err != nil {
|
|
return false, fmt.Errorf("windows server check: %w", err)
|
|
}
|
|
defer k.Close()
|
|
|
|
s, _, err := k.GetStringValue("InstallationType")
|
|
if err != nil {
|
|
return false, fmt.Errorf("windows server check: %w", err)
|
|
}
|
|
|
|
if s == "Server" {
|
|
return true, nil
|
|
}
|
|
|
|
return false, nil
|
|
}
|