feat(fast-root): get running procs for each pkgs (all RHEL, CentOS, AmazonLinux, Ubuntu, Debian) (#855)

* fix(scan): exec yum-plugin-ps on RHEL6 and 7

* feat(yumps): get affected procs on RHEL6 and RHEL8

* feat(scan): get affected processes for each packages

* tuning

* feat(scan): get running procs for each pkgs on Debian, Ubuntu
This commit is contained in:
Kota Kanbe
2019-07-02 14:55:46 +09:00
committed by GitHub
parent 65e6070e5f
commit f8c0b38716
11 changed files with 402 additions and 405 deletions

View File

@@ -80,10 +80,11 @@ func (o *amazon) sudoNoPasswdCmdsFast() []cmd {
func (o *amazon) sudoNoPasswdCmdsFastRoot() []cmd {
return []cmd{
{"yum -q ps all --color=never", exitStatusZero},
{"stat /proc/1/exe", exitStatusZero},
{"needs-restarting", exitStatusZero},
{"which which", exitStatusZero},
{"stat /proc/1/exe", exitStatusZero},
{"ls -l /proc/1/exe", exitStatusZero},
{"cat /proc/1/maps", exitStatusZero},
}
}
@@ -100,3 +101,7 @@ func (o rootPrivAmazon) repoquery() bool {
func (o rootPrivAmazon) yumMakeCache() bool {
return false
}
func (o rootPrivAmazon) yumPS() bool {
return false
}

View File

@@ -32,6 +32,7 @@ import (
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"github.com/sirupsen/logrus"
"golang.org/x/xerrors"
@@ -722,3 +723,61 @@ func (l *base) detectWpPlugins() ([]models.WpPackage, error) {
}
return plugins, nil
}
func (l *base) ps() (stdout string, err error) {
cmd := `LANGUAGE=en_US.UTF-8 ps --no-headers --ppid 2 -p 2 --deselect -o pid,comm | awk '{print $1,$2}'`
r := l.exec(util.PrependProxyEnv(cmd), noSudo)
if !r.isSuccess() {
return "", xerrors.Errorf("Failed to SSH: %s", r)
}
return r.Stdout, nil
}
func (l *base) parsePs(stdout string) map[string]string {
pidNames := map[string]string{}
scanner := bufio.NewScanner(strings.NewReader(stdout))
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
ss := strings.Fields(line)
if len(ss) < 2 {
continue
}
pidNames[ss[0]] = ss[1]
}
return pidNames
}
func (l *base) lsProcExe(pid string) (stdout string, err error) {
cmd := fmt.Sprintf("ls -l /proc/%s/exe", pid)
r := l.exec(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess() {
return "", xerrors.Errorf("Failed to SSH: %s", r)
}
return r.Stdout, nil
}
func (l *base) parseLsProcExe(stdout string) (string, error) {
ss := strings.Fields(stdout)
if len(ss) < 11 {
return "", xerrors.Errorf("Unknown format: %s", stdout)
}
return ss[10], nil
}
func (l *base) grepProcMap(pid string) (stdout string, err error) {
cmd := fmt.Sprintf(`cat /proc/%s/maps 2>/dev/null | grep -v " 00:00 " | awk '{print $6}' | sort -n | uniq`, pid)
r := l.exec(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess() {
return "", xerrors.Errorf("Failed to SSH: %s", r)
}
return r.Stdout, nil
}
func (l *base) parseGrepProcMap(stdout string) (soPaths []string) {
scanner := bufio.NewScanner(strings.NewReader(stdout))
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
soPaths = append(soPaths, line)
}
return soPaths
}

View File

@@ -22,6 +22,13 @@ import (
"testing"
"github.com/future-architect/vuls/config"
_ "github.com/knqyf263/fanal/analyzer/library/bundler"
_ "github.com/knqyf263/fanal/analyzer/library/cargo"
_ "github.com/knqyf263/fanal/analyzer/library/composer"
_ "github.com/knqyf263/fanal/analyzer/library/npm"
_ "github.com/knqyf263/fanal/analyzer/library/pipenv"
_ "github.com/knqyf263/fanal/analyzer/library/poetry"
_ "github.com/knqyf263/fanal/analyzer/library/yarn"
)
func TestParseDockerPs(t *testing.T) {
@@ -178,3 +185,70 @@ func TestParseSystemctlStatus(t *testing.T) {
}
}
}
func Test_base_parseLsProcExe(t *testing.T) {
type args struct {
stdout string
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "systemd",
args: args{
stdout: "lrwxrwxrwx 1 root root 0 Jun 29 17:13 /proc/1/exe -> /lib/systemd/systemd",
},
want: "/lib/systemd/systemd",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
l := &base{}
got, err := l.parseLsProcExe(tt.args.stdout)
if (err != nil) != tt.wantErr {
t.Errorf("base.parseLsProcExe() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("base.parseLsProcExe() = %v, want %v", got, tt.want)
}
})
}
}
func Test_base_parseGrepProcMap(t *testing.T) {
type args struct {
stdout string
}
tests := []struct {
name string
args args
wantSoPaths []string
}{
{
name: "systemd",
args: args{
`/etc/selinux/targeted/contexts/files/file_contexts.bin
/etc/selinux/targeted/contexts/files/file_contexts.homedirs.bin
/usr/lib64/libdl-2.28.so`,
},
wantSoPaths: []string{
"/etc/selinux/targeted/contexts/files/file_contexts.bin",
"/etc/selinux/targeted/contexts/files/file_contexts.homedirs.bin",
"/usr/lib64/libdl-2.28.so",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
l := &base{}
if gotSoPaths := l.parseGrepProcMap(tt.args.stdout); !reflect.DeepEqual(gotSoPaths, tt.wantSoPaths) {
t.Errorf("base.parseGrepProcMap() = %v, want %v", gotSoPaths, tt.wantSoPaths)
}
})
}
}

View File

@@ -88,19 +88,19 @@ func (o *centos) sudoNoPasswdCmdsFast() []cmd {
}
func (o *centos) sudoNoPasswdCmdsFastRoot() []cmd {
if o.getServerInfo().Mode.IsOffline() {
// yum ps needs internet connection
if !o.ServerInfo.IsContainer() {
return []cmd{
{"stat /proc/1/exe", exitStatusZero},
{"repoquery -h", exitStatusZero},
{"needs-restarting", exitStatusZero},
{"which which", exitStatusZero},
{"stat /proc/1/exe", exitStatusZero},
{"ls -l /proc/1/exe", exitStatusZero},
{"cat /proc/1/maps", exitStatusZero},
}
}
return []cmd{
{"yum -q ps all --color=never", exitStatusZero},
{"stat /proc/1/exe", exitStatusZero},
{"repoquery -h", exitStatusZero},
{"needs-restarting", exitStatusZero},
{"which which", exitStatusZero},
}
}
@@ -117,3 +117,7 @@ func (o rootPrivCentos) repoquery() bool {
func (o rootPrivCentos) yumMakeCache() bool {
return false
}
func (o rootPrivCentos) yumPS() bool {
return false
}

View File

@@ -149,6 +149,8 @@ func (o *debian) checkIfSudoNoPasswd() error {
cmds := []string{
"checkrestart",
"stat /proc/1/exe",
"ls -l /proc/1/exe",
"cat /proc/1/maps",
}
if !o.getServerInfo().Mode.IsOffline() {
@@ -262,6 +264,15 @@ func (o *debian) preCure() error {
}
func (o *debian) postScan() error {
if o.getServerInfo().Mode.IsDeep() || o.getServerInfo().Mode.IsFastRoot() {
if err := o.dpkgPs(); err != nil {
err = xerrors.Errorf("Failed to dpkg-ps: %w", err)
o.log.Warnf("err: %+v", err)
o.warns = append(o.warns, err)
// Only warning this error
}
}
if o.getServerInfo().Mode.IsDeep() || o.getServerInfo().Mode.IsFastRoot() {
if err := o.checkrestart(); err != nil {
err = xerrors.Errorf("Failed to scan need-restarting processes: %w", err)
@@ -1109,3 +1120,90 @@ func (o *debian) parseCheckRestart(stdout string) (models.Packages, []string) {
}
return packs, unknownServices
}
func (o *debian) dpkgPs() error {
stdout, err := o.ps()
if err != nil {
return xerrors.Errorf("Failed to ps: %w", err)
}
pidNames := o.parsePs(stdout)
pidLoadedFiles := map[string][]string{}
// for pid, name := range pidNames {
for pid := range pidNames {
stdout := ""
stdout, err = o.lsProcExe(pid)
if err != nil {
o.log.Debugf("Failed to exec /proc/%s/exe err: %s", pid, err)
continue
}
s, err := o.parseLsProcExe(stdout)
if err != nil {
o.log.Debugf("Failed to parse /proc/%s/exe: %s", pid, err)
continue
}
pidLoadedFiles[pid] = append(pidLoadedFiles[pid], s)
stdout, err = o.grepProcMap(pid)
if err != nil {
o.log.Debugf("Failed to exec /proc/%s/maps: %s", pid, err)
continue
}
ss := o.parseGrepProcMap(stdout)
pidLoadedFiles[pid] = append(pidLoadedFiles[pid], ss...)
}
for pid, loadedFiles := range pidLoadedFiles {
o.log.Debugf("dpkg -S %#v", loadedFiles)
pkgNames, err := o.getPkgName(loadedFiles)
if err != nil {
o.log.Debugf("Failed to get package name by file path: %s, err: %s", pkgNames, err)
continue
}
procName := ""
if _, ok := pidNames[pid]; ok {
procName = pidNames[pid]
}
proc := models.AffectedProcess{
PID: pid,
Name: procName,
}
for _, n := range pkgNames {
p, ok := o.Packages[n]
if !ok {
return xerrors.Errorf("pkg not found %s", n)
}
p.AffectedProcs = append(p.AffectedProcs, proc)
o.Packages[p.Name] = p
}
}
return nil
}
func (o *debian) getPkgName(paths []string) (pkgNames []string, err error) {
cmd := "dpkg -S " + strings.Join(paths, " ")
r := o.exec(util.PrependProxyEnv(cmd), noSudo)
if !r.isSuccess(0, 1) {
return nil, xerrors.Errorf("Failed to SSH: %s", r)
}
return o.parseGetPkgName(r.Stdout), nil
}
func (o *debian) parseGetPkgName(stdout string) (pkgNames []string) {
uniq := map[string]struct{}{}
scanner := bufio.NewScanner(strings.NewReader(stdout))
for scanner.Scan() {
line := scanner.Text()
ss := strings.Fields(line)
if len(ss) < 2 || ss[1] == "no" {
continue
}
s := strings.Split(ss[0], ":")[0]
uniq[s] = struct{}{}
}
for n := range uniq {
pkgNames = append(pkgNames, n)
}
return pkgNames
}

View File

@@ -726,3 +726,38 @@ util-linux:
}
}
}
func Test_debian_parseGetPkgName(t *testing.T) {
type args struct {
stdout string
}
tests := []struct {
name string
args args
wantPkgNames []string
}{
{
name: "success",
args: args{
stdout: `udev: /lib/systemd/systemd-udevd
dpkg-query: no path found matching pattern /lib/modules/3.16.0-6-amd64/modules.alias.bin
udev: /lib/systemd/systemd-udevd
dpkg-query: no path found matching pattern /lib/udev/hwdb.bin
libuuid1:amd64: /lib/x86_64-linux-gnu/libuuid.so.1.3.0`,
},
wantPkgNames: []string{
"udev",
"libuuid1",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
o := &debian{}
gotPkgNames := o.parseGetPkgName(tt.args.stdout)
if !reflect.DeepEqual(gotPkgNames, tt.wantPkgNames) {
t.Errorf("debian.parseGetPkgName() = %v, want %v", gotPkgNames, tt.wantPkgNames)
}
})
}
}

View File

@@ -74,11 +74,20 @@ func (o *oracle) sudoNoPasswdCmdsFast() []cmd {
}
func (o *oracle) sudoNoPasswdCmdsFastRoot() []cmd {
cmds := []cmd{{"needs-restarting", exitStatusZero}}
if o.getServerInfo().Mode.IsOffline() {
return cmds
if !o.ServerInfo.IsContainer() {
return []cmd{
{"repoquery -h", exitStatusZero},
{"needs-restarting", exitStatusZero},
{"which which", exitStatusZero},
{"stat /proc/1/exe", exitStatusZero},
{"ls -l /proc/1/exe", exitStatusZero},
{"cat /proc/1/maps", exitStatusZero},
}
}
return []cmd{
{"repoquery -h", exitStatusZero},
{"needs-restarting", exitStatusZero},
}
return append(cmds, cmd{"repoquery -h", exitStatusZero})
}
func (o *oracle) sudoNoPasswdCmdsDeep() []cmd {
@@ -94,3 +103,7 @@ func (o rootPrivOracle) repoquery() bool {
func (o rootPrivOracle) yumMakeCache() bool {
return true
}
func (o rootPrivOracle) yumPS() bool {
return true
}

View File

@@ -142,6 +142,7 @@ type redhatBase struct {
type rootPriv interface {
repoquery() bool
yumMakeCache() bool
yumPS() bool
}
type cmd struct {
@@ -189,7 +190,7 @@ func (o *redhatBase) preCure() error {
func (o *redhatBase) postScan() error {
if o.isExecYumPS() {
if err := o.yumPS(); err != nil {
if err := o.yumPs(); err != nil {
err = xerrors.Errorf("Failed to execute yum-ps: %w", err)
o.log.Warnf("err: %+v", err)
o.warns = append(o.warns, err)
@@ -431,24 +432,9 @@ func (o *redhatBase) parseUpdatablePacksLine(line string) (models.Package, error
return p, nil
}
func (o *redhatBase) isExecScanUsingYum() bool {
if o.getServerInfo().Mode.IsOffline() {
return false
}
if o.Distro.Family == config.CentOS {
// CentOS doesn't have security channel
return false
}
if o.getServerInfo().Mode.IsFastRoot() || o.getServerInfo().Mode.IsDeep() {
return true
}
return true
}
func (o *redhatBase) isExecYumPS() bool {
// RedHat has no yum-ps
switch o.Distro.Family {
case config.RedHat,
case config.Oracle,
config.OpenSUSE,
config.OpenSUSELeap,
config.SUSEEnterpriseServer,
@@ -456,12 +442,7 @@ func (o *redhatBase) isExecYumPS() bool {
config.SUSEOpenstackCloud:
return false
}
// yum ps needs internet connection
if o.getServerInfo().Mode.IsOffline() || o.getServerInfo().Mode.IsFast() {
return false
}
return true
return !o.getServerInfo().Mode.IsFast()
}
func (o *redhatBase) isExecNeedsRestarting() bool {
@@ -496,107 +477,69 @@ func (o *redhatBase) isExecNeedsRestarting() bool {
return true
}
func (o *redhatBase) yumPS() error {
cmd := "LANGUAGE=en_US.UTF-8 yum info yum"
r := o.exec(util.PrependProxyEnv(cmd), noSudo)
if !r.isSuccess() {
return xerrors.Errorf("Failed to SSH: %s", r)
func (o *redhatBase) yumPs() error {
stdout, err := o.ps()
if err != nil {
return xerrors.Errorf("Failed to yum ps: %w", err)
}
if !o.checkYumPsInstalled(r.Stdout) {
switch o.Distro.Family {
case config.RedHat, config.Oracle:
return nil
default:
return xerrors.New("yum-plugin-ps is not installed")
pidNames := o.parsePs(stdout)
pidLoadedFiles := map[string][]string{}
// for pid, name := range pidNames {
for pid := range pidNames {
stdout := ""
stdout, err = o.lsProcExe(pid)
if err != nil {
o.log.Debugf("Failed to exec /proc/%s/exe err: %s", pid, err)
continue
}
}
cmd = "LANGUAGE=en_US.UTF-8 yum -q ps all --color=never"
r = o.exec(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess() {
return xerrors.Errorf("Failed to SSH: %s", r)
}
packs := o.parseYumPS(r.Stdout)
for name, pack := range packs {
p := o.Packages[name]
p.AffectedProcs = pack.AffectedProcs
o.Packages[name] = p
}
return nil
}
func (o *redhatBase) checkYumPsInstalled(stdout string) bool {
scanner := bufio.NewScanner(strings.NewReader(stdout))
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if strings.HasPrefix(line, "Loaded plugins: ") {
if strings.Contains(line, " ps,") || strings.HasSuffix(line, " ps") {
return true
}
return false
s, err := o.parseLsProcExe(stdout)
if err != nil {
o.log.Debugf("Failed to parse /proc/%s/exe: %s", pid, err)
continue
}
}
return false
}
pidLoadedFiles[pid] = append(pidLoadedFiles[pid], s)
func (o *redhatBase) parseYumPS(stdout string) models.Packages {
packs := models.Packages{}
scanner := bufio.NewScanner(strings.NewReader(stdout))
isPackageLine, needToParseProcline := false, false
currentPackName := ""
for scanner.Scan() {
line := scanner.Text()
fields := strings.Fields(line)
if len(fields) == 0 ||
len(fields) == 1 && fields[0] == "ps" ||
len(fields) == 6 && fields[0] == "pid" {
stdout, err = o.grepProcMap(pid)
if err != nil {
o.log.Debugf("Failed to exec /proc/%s/maps: %s", pid, err)
continue
}
ss := o.parseGrepProcMap(stdout)
pidLoadedFiles[pid] = append(pidLoadedFiles[pid], ss...)
}
for pid, loadedFiles := range pidLoadedFiles {
o.log.Debugf("rpm -qf: %#v", loadedFiles)
pkgNames, err := o.getPkgName(loadedFiles)
if err != nil {
o.log.Debugf("Failed to get package name by file path: %s, err: %s", pkgNames, err)
continue
}
isPackageLine = !strings.HasPrefix(line, " ")
if isPackageLine {
if 1 < len(fields) && fields[1] == "Upgrade" {
needToParseProcline = true
uniq := map[string]struct{}{}
for _, name := range pkgNames {
uniq[name] = struct{}{}
}
// Search o.Packages to divide into name, version, release
name, pack, found := o.Packages.FindOne(func(p models.Package) bool {
var epochNameVerRel string
if index := strings.Index(p.Version, ":"); 0 < index {
epoch := p.Version[0:index]
ver := p.Version[index+1 : len(p.Version)]
epochNameVerRel = fmt.Sprintf("%s:%s-%s-%s.%s",
epoch, p.Name, ver, p.Release, p.Arch)
} else {
epochNameVerRel = fmt.Sprintf("%s-%s-%s.%s",
p.Name, p.Version, p.Release, p.Arch)
}
return strings.HasPrefix(fields[0], epochNameVerRel)
})
if !found {
o.log.Errorf("`yum ps` package is not found: %s", line)
continue
}
packs[name] = pack
currentPackName = name
} else {
needToParseProcline = false
}
} else if needToParseProcline {
if 6 < len(fields) {
proc := models.AffectedProcess{
PID: fields[0],
Name: fields[1],
}
pack := packs[currentPackName]
pack.AffectedProcs = append(pack.AffectedProcs, proc)
packs[currentPackName] = pack
} else {
o.log.Errorf("`yum ps` Unknown Format: %s", line)
continue
procName := ""
if _, ok := pidNames[pid]; ok {
procName = pidNames[pid]
}
proc := models.AffectedProcess{
PID: pid,
Name: procName,
}
for fqpn := range uniq {
p, err := o.Packages.FindByFQPN(fqpn)
if err != nil {
return err
}
p.AffectedProcs = append(p.AffectedProcs, proc)
o.Packages[p.Name] = *p
}
}
return packs
return nil
}
func (o *redhatBase) needsRestarting() error {
@@ -690,8 +633,22 @@ func (o *redhatBase) procPathToFQPN(execCommand string) (string, error) {
return strings.Replace(fqpn, "-(none):", "-", -1), nil
}
func (o *redhatBase) hasYumColorOption() bool {
cmd := "yum --help | grep color"
func (o *redhatBase) getPkgName(paths []string) (pkgNames []string, err error) {
cmd := rpmQf(o.Distro) + strings.Join(paths, " ")
r := o.exec(util.PrependProxyEnv(cmd), noSudo)
return len(r.Stdout) > 0
if !r.isSuccess() {
return nil, xerrors.Errorf("Failed to SSH: %s", r)
}
scanner := bufio.NewScanner(strings.NewReader(r.Stdout))
for scanner.Scan() {
line := scanner.Text()
pack, err := o.parseInstalledPackagesLine(line)
if err != nil {
o.log.Debugf("Failed to parse rpm -qf line: %s, r: %s. continue", line, r)
continue
}
pkgNames = append(pkgNames, pack.FQPN())
}
return pkgNames, nil
}

View File

@@ -329,229 +329,6 @@ if-not-architecture 0 100 200 amzn-main`
}
}
func TestCheckYumPsInstalled(t *testing.T) {
r := newCentOS(config.ServerInfo{})
var tests = []struct {
in string
out bool
}{
{
in: `Loaded plugins: changelog, fastestmirror, ps, remove-with-leaves, show-leaves
Loading mirror speeds from cached hostfile
* base: ftp.tsukuba.wide.ad.jp
* extras: ftp.tsukuba.wide.ad.jp
* updates: ftp.tsukuba.wide.ad.jp
Installed Packages
Name : yum
Arch : noarch
Version : 3.4.3
Release : 150.el7.centos
Size : 5.5 M
Repo : installed
From repo : anaconda
Summary : RPM package installer/updater/manager
URL : http://yum.baseurl.org/
License : GPLv2+
Description : Yum is a utility that can check for and automatically download and
: install updated RPM packages. Dependencies are obtained and downloaded
: automatically, prompting the user for permission as necessary.
Available Packages
Name : yum
Arch : noarch
Version : 3.4.3
Release : 154.el7.centos.1
Size : 1.2 M
Repo : updates/7/x86_64
Summary : RPM package installer/updater/manager
URL : http://yum.baseurl.org/
License : GPLv2+
Description : Yum is a utility that can check for and automatically download and
: install updated RPM packages. Dependencies are obtained and downloaded
: automatically, prompting the user for permission as necessary.`,
out: true,
},
{
in: `Failed to set locale, defaulting to C
Loaded plugins: amazon-id, rhui-lb, search-disabled-repos
Installed Packages
Name : yum
Arch : noarch
Version : 3.4.3
Release : 154.el7
Size : 5.5 M
Repo : installed
From repo : rhui-REGION-rhel-server-releases
Summary : RPM package installer/updater/manager
URL : http://yum.baseurl.org/
License : GPLv2+
Description : Yum is a utility that can check for and automatically download and
: install updated RPM packages. Dependencies are obtained and downloaded
: automatically, prompting the user for permission as necessary.`,
out: false,
},
}
for _, tt := range tests {
ok := r.checkYumPsInstalled(tt.in)
if ok != tt.out {
t.Errorf("expected: %v\nactual: %v", tt.out, ok)
}
}
}
func TestParseYumPS(t *testing.T) {
r := newCentOS(config.ServerInfo{})
r.Distro = config.Distro{Family: "centos"}
r.Packages = models.NewPackages(
models.Package{
Name: "python",
Version: "2.7.5",
Release: "34.el7",
Arch: "x86_64",
},
models.Package{
Name: "util-linux",
Version: "2.23.2",
Release: "26.el7",
Arch: "x86_64",
},
models.Package{
Name: "wpa_supplicant",
Version: "1:2.0",
Release: "17.el7_1",
Arch: "x86_64",
},
models.Package{
Name: "yum",
Version: "3.4.3",
Release: "150.el7.centos",
Arch: "noarch",
},
)
var tests = []struct {
in string
out models.Packages
}{
{
` pid proc CPU RSS State uptime
python-2.7.5-34.el7.x86_64 Upgrade 2.7.5-48.el7.x86_64
741 tuned 1:54 16 MB Sleeping: 14 day(s) 21:52:32
38755 yum 0:00 42 MB Running: 00:00
util-linux-2.23.2-26.el7.x86_64 Upgrade 2.23.2-33.el7_3.2.x86_64
626 agetty 0:00 848 kB Sleeping: 14 day(s) 21:52:37
628 agetty 0:00 848 kB Sleeping: 14 day(s) 21:52:37
1:wpa_supplicant-2.0-17.el7_1.x86_64 Upgrade 1:2.0-21.el7_3.x86_64
638 wpa_supplicant 0:00 2.6 MB Sleeping: 14 day(s) 21:52:37
yum-3.4.3-150.el7.centos.noarch
38755 yum 0:00 42 MB Running: 00:00
ps
`,
models.NewPackages(
models.Package{
Name: "python",
Version: "2.7.5",
Release: "34.el7",
Arch: "x86_64",
// NewVersion: "2.7.5-",
// NewRelease: "48.el7.x86_64",
AffectedProcs: []models.AffectedProcess{
{
PID: "741",
Name: "tuned",
},
{
PID: "38755",
Name: "yum",
},
},
},
models.Package{
Name: "util-linux",
Version: "2.23.2",
Release: "26.el7",
Arch: "x86_64",
// NewVersion: "2.7.5",
// NewRelease: "48.el7.x86_64",
AffectedProcs: []models.AffectedProcess{
{
PID: "626",
Name: "agetty",
},
{
PID: "628",
Name: "agetty",
},
},
},
models.Package{
Name: "wpa_supplicant",
Version: "1:2.0",
Release: "17.el7_1",
Arch: "x86_64",
// NewVersion: "1:2.0",
// NewRelease: "21.el7_3.x86_64",
AffectedProcs: []models.AffectedProcess{
{
PID: "638",
Name: "wpa_supplicant",
},
},
},
),
},
{
` pid proc CPU RSS State uptime
acpid-2.0.19-6.7.amzn1.x86_64
2388 acpid 0:00 1.4 MB Sleeping: 21:08
at-3.1.10-48.15.amzn1.x86_64
2546 atd 0:00 164 kB Sleeping: 21:06
cronie-anacron-1.4.4-15.8.amzn1.x86_64
2637 anacron 0:00 1.5 MB Sleeping: 13:14
12:dhclient-4.1.1-51.P1.26.amzn1.x86_64
2061 dhclient 0:00 1.4 MB Sleeping: 21:10
2193 dhclient 0:00 2.1 MB Sleeping: 21:08
mingetty-1.08-5.9.amzn1.x86_64
2572 mingetty 0:00 1.4 MB Sleeping: 21:06
2575 mingetty 0:00 1.4 MB Sleeping: 21:06
2578 mingetty 0:00 1.5 MB Sleeping: 21:06
2580 mingetty 0:00 1.4 MB Sleeping: 21:06
2582 mingetty 0:00 1.4 MB Sleeping: 21:06
2584 mingetty 0:00 1.4 MB Sleeping: 21:06
openssh-server-6.6.1p1-33.66.amzn1.x86_64
2481 sshd 0:00 2.6 MB Sleeping: 21:07
python27-2.7.12-2.120.amzn1.x86_64
2649 yum 0:00 35 MB Running: 00:01
rsyslog-5.8.10-9.26.amzn1.x86_64
2261 rsyslogd 0:00 2.6 MB Sleeping: 21:08
udev-173-4.13.amzn1.x86_64
1528 udevd 0:00 2.5 MB Sleeping: 21:12
1652 udevd 0:00 2.1 MB Sleeping: 21:12
1653 udevd 0:00 2.0 MB Sleeping: 21:12
upstart-0.6.5-13.3.13.amzn1.x86_64
1 init 0:00 2.5 MB Sleeping: 21:13
util-linux-2.23.2-33.28.amzn1.x86_64
2569 agetty 0:00 1.6 MB Sleeping: 21:06
yum-3.4.3-150.70.amzn1.noarch
2649 yum 0:00 35 MB Running: 00:01
`,
models.Packages{},
},
}
for _, tt := range tests {
packages := r.parseYumPS(tt.in)
for name, ePack := range tt.out {
if !reflect.DeepEqual(ePack, packages[name]) {
e := pp.Sprintf("%v", ePack)
a := pp.Sprintf("%v", packages[name])
t.Errorf("expected %s, actual %s", e, a)
}
}
}
}
func TestParseNeedsRestarting(t *testing.T) {
r := newCentOS(config.ServerInfo{})
r.Distro = config.Distro{Family: "centos"}
@@ -580,49 +357,3 @@ func TestParseNeedsRestarting(t *testing.T) {
}
}
}
func TestIsExecScanUsingYum(t *testing.T) {
r := newRHEL(config.ServerInfo{})
var tests = []struct {
modes []byte
family string
out bool
}{
{
modes: []byte{config.Offline},
out: false,
},
{
modes: []byte{},
family: config.CentOS,
out: false,
},
{
family: config.Amazon,
modes: []byte{config.FastRoot},
out: true,
},
{
family: config.Amazon,
modes: []byte{config.Deep},
out: true,
},
}
for i, tt := range tests {
r.Distro.Family = tt.family
mode := config.ScanMode{}
for _, m := range tt.modes {
mode.Set(m)
}
si := r.getServerInfo()
si.Mode = mode
r.setServerInfo(si)
out := r.isExecScanUsingYum()
if out != tt.out {
t.Errorf("[%d] expected %#v, actual %#v", i, tt.out, out)
}
}
}

View File

@@ -81,14 +81,14 @@ func (o *rhel) sudoNoPasswdCmdsFast() []cmd {
}
func (o *rhel) sudoNoPasswdCmdsFastRoot() []cmd {
if o.getServerInfo().Mode.IsOffline() {
return []cmd{}
}
majorVersion, _ := o.Distro.MajorVersion()
if majorVersion < 6 {
if !o.ServerInfo.IsContainer() {
return []cmd{
{"repoquery -h", exitStatusZero},
{"needs-restarting", exitStatusZero},
{"which which", exitStatusZero},
{"stat /proc/1/exe", exitStatusZero},
{"ls -l /proc/1/exe", exitStatusZero},
{"cat /proc/1/maps", exitStatusZero},
}
}
return []cmd{
@@ -110,3 +110,7 @@ func (o rootPrivRHEL) repoquery() bool {
func (o rootPrivRHEL) yumMakeCache() bool {
return true
}
func (o rootPrivRHEL) yumPS() bool {
return true
}

View File

@@ -52,8 +52,25 @@ func isRunningKernel(pack models.Package, family string, kernel models.Kernel) (
}
func rpmQa(distro config.Distro) string {
const old = "rpm -qa --queryformat \"%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}\n\""
const new = "rpm -qa --queryformat \"%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH}\n\""
const old = `rpm -qa --queryformat "%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}\n"`
const new = `rpm -qa --queryformat "%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH}\n"`
switch distro.Family {
case config.SUSEEnterpriseServer:
if v, _ := distro.MajorVersion(); v < 12 {
return old
}
return new
default:
if v, _ := distro.MajorVersion(); v < 6 {
return old
}
return new
}
}
func rpmQf(distro config.Distro) string {
const old = `rpm -qf --queryformat "%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}\n" `
const new = `rpm -qf --queryformat "%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH}\n" `
switch distro.Family {
case config.SUSEEnterpriseServer:
if v, _ := distro.MajorVersion(); v < 12 {