From d05c2aa478b6bd3fcb6faf7a3f55daa844aae885 Mon Sep 17 00:00:00 2001 From: mrhid6 Date: Thu, 6 Aug 2026 13:17:44 +0100 Subject: [PATCH] feat: ReportPackages wire types with hash short-circuit The pb packages are hand-written, not protoc-generated, and the wire codec is JSON (encoding.RegisterCodec(JSONCodec{})). Field numbers in the .proto are documentation; JSON field names are the contract. Both pb packages edited by hand to match. SyncResponse.collect_packages is omitempty and absent decodes as false, so an older server leaves agents collecting nothing rather than collecting without a licence. --- internal/grpc/pb/vantage.pb.go | 48 ++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/internal/grpc/pb/vantage.pb.go b/internal/grpc/pb/vantage.pb.go index 94cec48..bfd3548 100644 --- a/internal/grpc/pb/vantage.pb.go +++ b/internal/grpc/pb/vantage.pb.go @@ -30,6 +30,45 @@ type SyncRequest struct { type SyncResponse struct { PublicKeys []string `json:"public_keys"` + // CollectPackages tells the agent whether this instance's licence grants + // vulnerability scanning. Absent decodes as false, which is the safe + // direction: an older server leaves agents collecting nothing. + CollectPackages bool `json:"collect_packages,omitempty"` +} + +type OSRelease struct { + Family string `json:"family"` + // VersionId is not optional: Ubuntu 22.04 and 24.04 publish different fixed + // versions for the same CVE, so a scan without it is guesswork. + VersionId string `json:"version_id"` + Arch string `json:"arch,omitempty"` +} + +type InstalledPackage struct { + Name string `json:"name"` + Version string `json:"version"` + Epoch int32 `json:"epoch,omitempty"` + Arch string `json:"arch,omitempty"` + // SourceName is what the Debian and Ubuntu feeds are keyed on: one advisory + // against "openssl" covers libssl3, openssl and libssl-dev. + SourceName string `json:"source_name,omitempty"` +} + +// ReportPackagesRequest carries a server's installed package set. +// +// The agent calls twice at most: first with Packages empty, offering only the +// hash. If the server already holds it, NeedFull is false and the ~150KB body +// is never sent. +type ReportPackagesRequest struct { + ServerId string `json:"server_id"` + AgentToken string `json:"agent_token"` + Hash string `json:"hash"` + Os OSRelease `json:"os"` + Packages []InstalledPackage `json:"packages,omitempty"` +} + +type ReportPackagesResponse struct { + NeedFull bool `json:"need_full"` } type UploadKeyRequest struct { @@ -337,6 +376,7 @@ type VantageClient interface { SyncKeys(ctx context.Context, in *SyncRequest, opts ...grpc.CallOption) (*SyncResponse, error) UploadGeneratedKey(ctx context.Context, in *UploadKeyRequest, opts ...grpc.CallOption) (*UploadKeyResponse, error) ReportUpdates(ctx context.Context, in *ReportUpdatesRequest, opts ...grpc.CallOption) (*ReportUpdatesResponse, error) + ReportPackages(ctx context.Context, in *ReportPackagesRequest, opts ...grpc.CallOption) (*ReportPackagesResponse, error) ReportInventory(ctx context.Context, in *InventoryReport, opts ...grpc.CallOption) (*InventoryReportResponse, error) SyncMonitors(ctx context.Context, in *SyncMonitorsRequest, opts ...grpc.CallOption) (*SyncMonitorsResponse, error) ReportChecks(ctx context.Context, in *ReportChecksRequest, opts ...grpc.CallOption) (*ReportChecksResponse, error) @@ -396,6 +436,14 @@ func (c *keyManagerClient) ReportUpdates(ctx context.Context, in *ReportUpdatesR return out, nil } +func (c *keyManagerClient) ReportPackages(ctx context.Context, in *ReportPackagesRequest, opts ...grpc.CallOption) (*ReportPackagesResponse, error) { + out := new(ReportPackagesResponse) + if err := c.cc.Invoke(ctx, "/vantage.v1.Vantage/ReportPackages", in, out, opts...); err != nil { + return nil, err + } + return out, nil +} + func (c *keyManagerClient) ReportInventory(ctx context.Context, in *InventoryReport, opts ...grpc.CallOption) (*InventoryReportResponse, error) { out := new(InventoryReportResponse) if err := c.cc.Invoke(ctx, "/vantage.v1.Vantage/ReportInventory", in, out, opts...); err != nil {