Skip to content

Commit 21cb7cf

Browse files
authored
Merge pull request #42 from swarit-stepsecurity/swarit/feat/linux-pkgs-support
chore(linux): wire up sys package support
2 parents 201fb79 + 6aa92ff commit 21cb7cf

2 files changed

Lines changed: 92 additions & 4 deletions

File tree

internal/model/model.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,18 @@ type PythonPackage struct {
149149
Version string `json:"version"`
150150
}
151151

152+
// SystemPackageScanResult holds parsed system package data for enterprise telemetry.
153+
// Unlike BrewScanResult (which sends raw base64), this sends pre-parsed packages
154+
// since syspkg.go already handles the format-specific parsing edge cases.
155+
type SystemPackageScanResult struct {
156+
ScanType string `json:"scan_type"` // "rpm", "dpkg", "pacman", "apk", "snap", "flatpak"
157+
PackageManager *PkgManager `json:"package_manager,omitempty"`
158+
Packages []SystemPackage `json:"packages"`
159+
PackagesCount int `json:"packages_count"`
160+
Error string `json:"error,omitempty"`
161+
ScanDurationMs int64 `json:"scan_duration_ms"`
162+
}
163+
152164
// BrewScanResult holds raw Homebrew scan output for enterprise telemetry.
153165
type BrewScanResult struct {
154166
ScanType string `json:"scan_type"` // "formulae" or "casks"

internal/telemetry/telemetry.go

Lines changed: 80 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,11 @@ type Payload struct {
4141
NodeProjects []model.NodeScanResult `json:"node_projects"`
4242
BrewPkgManager *model.PkgManager `json:"brew_package_manager,omitempty"`
4343
BrewScans []model.BrewScanResult `json:"brew_scans"`
44-
PythonPkgManagers []model.PkgManager `json:"python_package_managers"`
45-
PythonGlobalPackages []model.PythonScanResult `json:"python_global_packages"`
46-
PythonProjects []model.ProjectInfo `json:"python_projects"`
47-
AIAgents []model.AITool `json:"ai_agents"`
44+
PythonPkgManagers []model.PkgManager `json:"python_package_managers"`
45+
PythonGlobalPackages []model.PythonScanResult `json:"python_global_packages"`
46+
PythonProjects []model.ProjectInfo `json:"python_projects"`
47+
SystemPackageScans []model.SystemPackageScanResult `json:"system_package_scans"`
48+
AIAgents []model.AITool `json:"ai_agents"`
4849
MCPConfigs []model.MCPConfigEnterprise `json:"mcp_configs"`
4950

5051
ExecutionLogs *ExecutionLogs `json:"execution_logs,omitempty"`
@@ -68,6 +69,7 @@ type PerformanceMetrics struct {
6869
BrewCasksCount int `json:"brew_casks_count"`
6970
PythonGlobalPkgsCount int `json:"python_global_packages_count"`
7071
PythonProjectsCount int `json:"python_projects_count"`
72+
SystemPackagesCount int `json:"system_packages_count"`
7173
}
7274

7375
// Run executes enterprise telemetry: scan, build payload, upload to S3.
@@ -287,6 +289,67 @@ func Run(exec executor.Executor, log *progress.Logger, cfg *cli.Config) error {
287289
fmt.Fprintln(os.Stderr)
288290
}
289291

292+
// System package scanning (Linux only — rpm, dpkg, pacman, apk, snap, flatpak)
293+
var systemPackageScans []model.SystemPackageScanResult
294+
295+
if exec.GOOS() == model.PlatformLinux {
296+
log.Progress("Detecting system packages...")
297+
sysPkgDetector := detector.NewSystemPkgDetector(userExec)
298+
299+
// Primary system PM (rpm, dpkg, pacman, or apk)
300+
if pm := sysPkgDetector.Detect(ctx); pm != nil {
301+
log.Progress(" Found: %s v%s at %s", pm.Name, pm.Version, pm.Path)
302+
start := time.Now()
303+
packages := sysPkgDetector.ListPackages(ctx)
304+
duration := time.Since(start).Milliseconds()
305+
if packages == nil {
306+
packages = []model.SystemPackage{}
307+
}
308+
systemPackageScans = append(systemPackageScans, model.SystemPackageScanResult{
309+
ScanType: pm.Name,
310+
PackageManager: pm,
311+
Packages: packages,
312+
PackagesCount: len(packages),
313+
ScanDurationMs: duration,
314+
})
315+
log.Progress(" %s: %d packages in %dms", pm.Name, len(packages), duration)
316+
}
317+
318+
// Additional PMs (snap, flatpak) — coexist with system PM
319+
for _, mgr := range sysPkgDetector.DetectAdditionalManagers(ctx) {
320+
mgr := mgr
321+
log.Progress(" Found: %s v%s at %s", mgr.Name, mgr.Version, mgr.Path)
322+
start := time.Now()
323+
var packages []model.SystemPackage
324+
switch mgr.Name {
325+
case "snap":
326+
packages = sysPkgDetector.ListSnapPackages(ctx)
327+
case "flatpak":
328+
packages = sysPkgDetector.ListFlatpakPackages(ctx)
329+
}
330+
duration := time.Since(start).Milliseconds()
331+
if packages == nil {
332+
packages = []model.SystemPackage{}
333+
}
334+
systemPackageScans = append(systemPackageScans, model.SystemPackageScanResult{
335+
ScanType: mgr.Name,
336+
PackageManager: &mgr,
337+
Packages: packages,
338+
PackagesCount: len(packages),
339+
ScanDurationMs: duration,
340+
})
341+
log.Progress(" %s: %d packages in %dms", mgr.Name, len(packages), duration)
342+
}
343+
344+
if len(systemPackageScans) == 0 {
345+
log.Progress(" No system package managers found")
346+
}
347+
fmt.Fprintln(os.Stderr)
348+
} else {
349+
log.Progress("System package scanning: skipped (non-Linux)")
350+
fmt.Fprintln(os.Stderr)
351+
}
352+
290353
// Node.js scanning
291354
npmEnabled := true
292355
if cfg.EnableNPMScan != nil {
@@ -345,6 +408,9 @@ func Run(exec executor.Executor, log *progress.Logger, cfg *cli.Config) error {
345408
if pythonProjects == nil {
346409
pythonProjects = []model.ProjectInfo{}
347410
}
411+
if systemPackageScans == nil {
412+
systemPackageScans = []model.SystemPackageScanResult{}
413+
}
348414

349415
// Finalize execution logs before building payload
350416
execLogsBase64 := capture.Finalize()
@@ -373,6 +439,7 @@ func Run(exec executor.Executor, log *progress.Logger, cfg *cli.Config) error {
373439
PythonPkgManagers: pythonPkgManagers,
374440
PythonGlobalPackages: pythonGlobalPkgs,
375441
PythonProjects: pythonProjects,
442+
SystemPackageScans: systemPackageScans,
376443
AIAgents: allAI,
377444
MCPConfigs: mcpConfigs,
378445

@@ -393,6 +460,7 @@ func Run(exec executor.Executor, log *progress.Logger, cfg *cli.Config) error {
393460
BrewCasksCount: brewCasksCount(brewScans),
394461
PythonGlobalPkgsCount: len(pythonGlobalPkgs),
395462
PythonProjectsCount: len(pythonProjects),
463+
SystemPackagesCount: totalSystemPackagesCount(systemPackageScans),
396464
},
397465
}
398466

@@ -425,6 +493,14 @@ func brewCasksCount(scans []model.BrewScanResult) int {
425493
return 0
426494
}
427495

496+
func totalSystemPackagesCount(scans []model.SystemPackageScanResult) int {
497+
total := 0
498+
for _, s := range scans {
499+
total += s.PackagesCount
500+
}
501+
return total
502+
}
503+
428504
func uploadToS3(ctx context.Context, log *progress.Logger, payload *Payload) error {
429505
payloadJSON, err := json.Marshal(payload)
430506
if err != nil {

0 commit comments

Comments
 (0)