diff --git a/agent/internal/packages/osrelease.go b/agent/internal/packages/osrelease.go new file mode 100644 index 0000000..bdc2541 --- /dev/null +++ b/agent/internal/packages/osrelease.go @@ -0,0 +1,63 @@ +package packages + +import ( + "bufio" + "errors" + "io" + "os" + "runtime" + "strings" +) + +// OSRelease identifies the distribution well enough to select an advisory +// feed. VersionID is not optional: Ubuntu 22.04 and 24.04 publish different +// fixed versions for the same CVE. +type OSRelease struct { + Family string + VersionID string + Arch string +} + +// ParseOSRelease reads the os-release format: KEY=value, one per line, with +// values optionally quoted, and # comments. +// +// The quote stripping handles both ID=ubuntu and ID="rocky", which real +// distributions both emit. +func ParseOSRelease(r io.Reader) (OSRelease, error) { + out := OSRelease{Arch: runtime.GOARCH} + sc := bufio.NewScanner(r) + for sc.Scan() { + line := strings.TrimSpace(sc.Text()) + if line == "" || strings.HasPrefix(line, "#") { + continue + } + key, val, ok := strings.Cut(line, "=") + if !ok { + continue + } + val = strings.Trim(strings.TrimSpace(val), `"'`) + switch strings.TrimSpace(key) { + case "ID": + out.Family = strings.ToLower(val) + case "VERSION_ID": + out.VersionID = val + } + } + if err := sc.Err(); err != nil { + return OSRelease{}, err + } + if out.Family == "" { + return OSRelease{}, errors.New("os-release has no ID") + } + return out, nil +} + +// DetectOS reads /etc/os-release. +func DetectOS() (OSRelease, error) { + f, err := os.Open("/etc/os-release") + if err != nil { + return OSRelease{}, err + } + defer f.Close() + return ParseOSRelease(f) +}