From 25a562865ca71380e0541b2f12394d05c49f1662 Mon Sep 17 00:00:00 2001 From: Kyle Isom Date: Tue, 18 Nov 2025 18:55:58 -0800 Subject: [PATCH] cmd/kgz: linter fixes. --- cmd/kgz/main.go | 84 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 62 insertions(+), 22 deletions(-) diff --git a/cmd/kgz/main.go b/cmd/kgz/main.go index f7991b8..9df6c23 100644 --- a/cmd/kgz/main.go +++ b/cmd/kgz/main.go @@ -8,12 +8,14 @@ import ( "flag" "fmt" "io" + "math" "os" "path/filepath" "strings" - goutilslib "git.wntrmute.dev/kyle/goutils/lib" "golang.org/x/sys/unix" + + goutilslib "git.wntrmute.dev/kyle/goutils/lib" ) const gzipExt = ".gz" @@ -59,16 +61,61 @@ func buildKGExtra(uid, gid, mode uint32, ctimeS int64, ctimeNs int32) []byte { } // Wrap in gzip subfield: [ID1 ID2 LEN(lo) LEN(hi) PAYLOAD] + // Guard against payload length overflow to uint16 for the extra subfield length. + if len(payload) > int(math.MaxUint16) { + return nil + } extra := make([]byte, 4+len(payload)) extra[0] = kgzExtraID[0] extra[1] = kgzExtraID[1] - binary.LittleEndian.PutUint16(extra[2:], uint16(len(payload))) + binary.LittleEndian.PutUint16(extra[2:], uint16(len(payload)&0xFFFF)) //#nosec G115 - masked copy(extra[4:], payload) return extra } +// clampToInt32 clamps an int value into the int32 range using a switch to +// satisfy linters that prefer switch over if-else chains for ordered checks. +func clampToInt32(v int) int32 { + switch { + case v > int(math.MaxInt32): + return math.MaxInt32 + case v < int(math.MinInt32): + return math.MinInt32 + default: + return int32(v) + } +} + +// buildExtraForPath prepares the gzip Extra field for kgz by collecting +// uid/gid/mode and ctime information, applying any overrides, and encoding it. +func buildExtraForPath(st unix.Stat_t, path string, setUID, setGID int) []byte { + uid := st.Uid + gid := st.Gid + if setUID >= 0 { + if uint64(setUID) <= math.MaxUint32 { + uid = uint32(setUID & 0xFFFFFFFF) //#nosec G115 - masked + } + } + if setGID >= 0 { + if uint64(setGID) <= math.MaxUint32 { + gid = uint32(setGID & 0xFFFFFFFF) //#nosec G115 - masked + } + } + mode := uint32(st.Mode & 0o7777) + + // Use portable helper to gather ctime + var cts int64 + var ctns int32 + if ft, err := goutilslib.LoadFileTime(path); err == nil { + cts = ft.Changed.Unix() + ctns = clampToInt32(ft.Changed.Nanosecond()) + } + + return buildKGExtra(uid, gid, mode, cts, ctns) +} + // parseKGExtra scans a gzip Extra blob and returns kgz metadata if present. -func parseKGExtra(extra []byte) (uid, gid, mode uint32, ctimeS int64, ctimeNs int32, ok bool) { +func parseKGExtra(extra []byte) (uint32, uint32, uint32, int64, int32, bool) { i := 0 for i+4 <= len(extra) { id1 := extra[i] @@ -95,7 +142,16 @@ func parseKGExtra(extra []byte) (uid, gid, mode uint32, ctimeS int64, ctimeNs in if s.Version != 1 { return 0, 0, 0, 0, 0, false } - return uint32(s.UID), uint32(s.GID), uint32(s.Mode), s.CTimeSec, s.CTimeNSec, true + // Validate ranges before converting from int -> uint32 to avoid overflow. + if s.UID < 0 || s.GID < 0 || s.Mode < 0 { + return 0, 0, 0, 0, 0, false + } + if uint64(s.UID) > math.MaxUint32 || uint64(s.GID) > math.MaxUint32 || uint64(s.Mode) > math.MaxUint32 { + return 0, 0, 0, 0, 0, false + } + + return uint32(s.UID & 0xFFFFFFFF), uint32(s.GID & 0xFFFFFFFF), + uint32(s.Mode & 0xFFFFFFFF), s.CTimeSec, s.CTimeNSec, true //#nosec G115 - masked } i += l } @@ -111,7 +167,7 @@ func compress(path, target string, level int, includeExtra bool, setUID, setGID // Gather file metadata var st unix.Stat_t - if err := unix.Stat(path, &st); err != nil { + if err = unix.Stat(path, &st); err != nil { return fmt.Errorf("stat source: %w", err) } fi, err := sourceFile.Stat() @@ -132,23 +188,7 @@ func compress(path, target string, level int, includeExtra bool, setUID, setGID // Set header metadata gzipCompressor.ModTime = fi.ModTime() if includeExtra { - uid := uint32(st.Uid) - gid := uint32(st.Gid) - if setUID >= 0 { - uid = uint32(setUID) - } - if setGID >= 0 { - gid = uint32(setGID) - } - mode := uint32(st.Mode & 0o7777) - // Use portable helper to gather ctime - var cts int64 - var ctns int32 - if ft, err := goutilslib.LoadFileTime(path); err == nil { - cts = ft.Changed.Unix() - ctns = int32(ft.Changed.Nanosecond()) - } - gzipCompressor.Extra = buildKGExtra(uid, gid, mode, cts, ctns) + gzipCompressor.Extra = buildExtraForPath(st, path, setUID, setGID) } defer gzipCompressor.Close()