diff --git a/providers/os/resources/users/etcpasswd.go b/providers/os/resources/users/etcpasswd.go index 1c7266af67..1c340d25db 100644 --- a/providers/os/resources/users/etcpasswd.go +++ b/providers/os/resources/users/etcpasswd.go @@ -32,11 +32,15 @@ func ParseEtcPasswd(input io.Reader) ([]*User, error) { // parse uid uid, err := strconv.ParseInt(m[2], 10, 0) if err != nil { - log.Error().Err(err).Str("user", m[0]).Msg("could not parse uid") + // Skip the entry rather than fall through with uid 0: a + // malformed line must not surface as a phantom root account. + log.Error().Err(err).Str("user", m[0]).Msg("could not parse uid, skipping user") + continue } gid, err := strconv.ParseInt(m[3], 10, 0) if err != nil { - log.Error().Err(err).Str("user", m[0]).Msg("could not parse gid") + log.Error().Err(err).Str("user", m[0]).Msg("could not parse gid, skipping user") + continue } // bin:x:1:1:bin:/bin:/sbin/nologin diff --git a/providers/os/resources/users/etcpasswd_test.go b/providers/os/resources/users/etcpasswd_test.go index 0f636292f7..d3a2a7b9d4 100644 --- a/providers/os/resources/users/etcpasswd_test.go +++ b/providers/os/resources/users/etcpasswd_test.go @@ -4,6 +4,7 @@ package users_test import ( + "strings" "testing" "github.com/stretchr/testify/assert" @@ -13,6 +14,26 @@ import ( "go.mondoo.com/mql/v13/providers/os/resources/users" ) +func TestParseEtcPasswdSkipsMalformedUidGid(t *testing.T) { + // A line with a non-numeric uid/gid must be skipped, not surfaced as a + // phantom uid 0 (root) account. + const passwd = `root:x:0:0:root:/root:/bin/bash +broken:x:notanumber:1:broken uid:/home/broken:/bin/sh +brokengid:x:1001:notanumber:broken gid:/home/brokengid:/bin/sh +alice:x:1000:1000:Alice:/home/alice:/bin/bash +` + + m, err := users.ParseEtcPasswd(strings.NewReader(passwd)) + require.NoError(t, err) + require.Equal(t, 2, len(m), "malformed uid/gid lines should be skipped") + + assert.Equal(t, "root", m[0].Name) + assert.Equal(t, int64(0), m[0].Uid) + assert.Equal(t, "alice", m[1].Name) + assert.Equal(t, int64(1000), m[1].Uid) + assert.Equal(t, int64(1000), m[1].Gid) +} + func TestParseLinuxEtcPasswd(t *testing.T) { mock, err := mock.New(0, &inventory.Asset{}, mock.WithPath("./testdata/debian.toml")) if err != nil {