Skip to content

chore(macOS/linux): add sys pkgs metadata#43

Merged
ashishkurmi merged 5 commits intostep-security:mainfrom
swarit-stepsecurity:swarit/feat/sys-pkg-metadata
Apr 29, 2026
Merged

chore(macOS/linux): add sys pkgs metadata#43
ashishkurmi merged 5 commits intostep-security:mainfrom
swarit-stepsecurity:swarit/feat/sys-pkg-metadata

Conversation

@swarit-stepsecurity
Copy link
Copy Markdown
Member

What does this PR do?

Type of change

  • Bug fix
  • Enhancement
  • Documentation

Testing

  • Tested on macOS (version: ___)
  • Binary runs without errors: ./stepsecurity-dev-machine-guard --verbose
  • JSON output is valid: ./stepsecurity-dev-machine-guard --json | python3 -m json.tool
  • No secrets or credentials included
  • Lint passes: make lint
  • Tests pass: make test

Related Issues

@swarit-stepsecurity swarit-stepsecurity changed the title chore(linux): add sys pkgs metadata chore(macOS/linux): add sys pkgs metadata Apr 28, 2026
@ashishkurmi ashishkurmi requested a review from Copilot April 28, 2026 08:29
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds richer metadata collection for macOS Homebrew packages and Linux system packages, expanding the JSON model to include provenance/trust and install/build details.

Changes:

  • Switch Homebrew scanning to richer brew info --json=v2-based collection with fallbacks.
  • Expand SystemPackage and BrewPackage models to include additional metadata fields.
  • Enhance Linux system package detection/parsing for rpm/dpkg/pacman/apk, plus richer snap/flatpak fields.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
internal/scan/scanner.go Uses new rich Homebrew listing methods when brew scanning is enabled.
internal/model/model.go Extends package JSON models with richer metadata fields.
internal/detector/syspkg.go Adds richer per-package parsing for rpm/dpkg/pacman/apk and improves snap/flatpak parsing.
internal/detector/brew.go Implements rich Homebrew metadata collection (JSON + receipt enrichment fallback).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/detector/brew.go Outdated
func (d *BrewDetector) brewPrefix() string {
// Standard locations
for _, p := range []string{"/opt/homebrew", "/usr/local", "/home/linuxbrew/.linuxbrew"} {
if d.exec.FileExists(p + "/Cellar") {
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

brewPrefix() checks FileExists(p + "/Cellar"), but FileExists is implemented to return false for directories (see internal/executor/executor.go:120-123). Since Cellar is a directory, this makes brewPrefix() always return "", so receipt enrichment never runs. Use DirExists(p + "/Cellar") (and/or also consider checking for /Caskroom for cask-only installs).

Suggested change
if d.exec.FileExists(p + "/Cellar") {
if d.exec.DirExists(p+"/Cellar") || d.exec.DirExists(p+"/Caskroom") {

Copilot uses AI. Check for mistakes.
Comment thread internal/detector/syspkg.go Outdated
Comment on lines +415 to +419
// parts[6] = Section — dpkg category (e.g. "libs", "non-free/libs").
// "non-free" prefix signals non-open-source license.
if len(parts) >= 7 && parts[6] != "" {
pkg.License = parts[6] // section doubles as license signal for dpkg
}
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parseDpkgLine assigns the dpkg Section field to SystemPackage.License. Section is a package category (e.g., "libs", "devel", "non-free/*"), not a license expression, so this will produce misleading license values in JSON. Consider leaving License empty for dpkg (unless you can reliably extract it), or add a dedicated field (e.g. Section) instead of overloading License.

Suggested change
// parts[6] = Section — dpkg category (e.g. "libs", "non-free/libs").
// "non-free" prefix signals non-open-source license.
if len(parts) >= 7 && parts[6] != "" {
pkg.License = parts[6] // section doubles as license signal for dpkg
}
// parts[6] = Section — a dpkg package category (for example "libs" or
// "non-free/libs"), not a license expression, so do not map it to License.

Copilot uses AI. Check for mistakes.
Comment thread internal/detector/brew.go
Comment on lines +93 to +118
// ListFormulaeRich returns installed formulae with metadata.
// Uses two sources:
// 1. brew info --json=v2 for desc/license/homepage (subprocess, but comprehensive)
// 2. INSTALL_RECEIPT.json from Cellar for tap/install_time/dependency (file read, fast)
//
// Falls back gracefully: if JSON command fails, reads receipts only.
// If receipts fail, falls back to basic `brew list --versions`.
func (d *BrewDetector) ListFormulaeRich(ctx context.Context) []model.BrewPackage {
// Try brew info --json=v2 first (gets everything in one shot)
stdout, _, exitCode, err := d.exec.RunWithTimeout(ctx, 60*time.Second,
"brew", "info", "--json=v2", "--installed", "--formula")
if err == nil && exitCode == 0 {
pkgs, parseErr := parseBrewInfoJSON(stdout, "formula")
if parseErr == nil && len(pkgs) > 0 {
return pkgs
}
}

// Fallback: basic list + receipt enrichment
pkgs := d.ListFormulae(ctx)
if len(pkgs) == 0 {
return nil
}
d.enrichFromReceipts(pkgs, "formula")
return pkgs
}
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new rich Homebrew scanning path (ListFormulaeRich/ListCasksRich + parseBrewInfoJSON + receipt enrichment) changes output semantics and parsing significantly, but existing tests only cover the basic brew list --versions path. Adding unit tests for JSON parsing and the receipt-enrichment fallback (using executor.Mock file stubs) would help prevent regressions across brew versions/output variants.

Copilot uses AI. Check for mistakes.
@swarit-stepsecurity swarit-stepsecurity force-pushed the swarit/feat/sys-pkg-metadata branch from 2a68eee to dafec65 Compare April 28, 2026 09:17
@shubham-stepsecurity
Copy link
Copy Markdown
Member

LGTM 🚀

Signed-off-by: Swarit Pandey <swarit@stepsecurity.io>
Signed-off-by: Swarit Pandey <swarit@stepsecurity.io>
Signed-off-by: Swarit Pandey <swarit@stepsecurity.io>
Signed-off-by: Swarit Pandey <swarit@stepsecurity.io>
@swarit-stepsecurity swarit-stepsecurity force-pushed the swarit/feat/sys-pkg-metadata branch from dafec65 to feb81aa Compare April 29, 2026 03:33
- brew.go: use DirExists instead of FileExists for Cellar/Caskroom
- syspkg.go: map dpkg Section to dedicated Section field, not License
- model.go: add Section field to SystemPackage
- brew_rich_test.go: add tests for rich brew metadata paths

Signed-off-by: Swarit Pandey <swarit@stepsecurity.io>
@swarit-stepsecurity swarit-stepsecurity force-pushed the swarit/feat/sys-pkg-metadata branch from feb81aa to 56b84b5 Compare April 29, 2026 04:15
@ashishkurmi ashishkurmi merged commit cbf40f9 into step-security:main Apr 29, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants