func TestSplitIntoBlocks(t *testing.T) { var tests = []struct { in string expected []string }{ { ` block1 block2 block2 block2 block3 block3`, []string{ `block1`, "block2\nblock2\nblock2", "block3\nblock3", }, }, } d := newBsd(config.ServerInfo{}) for _, tt := range tests { actual := d.splitIntoBlocks(tt.in) if !reflect.DeepEqual(tt.expected, actual) { e := pp.Sprintf("%v", tt.expected) a := pp.Sprintf("%v", actual) t.Errorf("expected %s, actual %s", e, a) } } }
func TestSplitAptCachePolicy(t *testing.T) { var tests = []struct { stdout string expected map[string]string }{ // This function parse apt-cache policy by using Regexp multi-line mode. // So, test data includes "\r\n" { "apt:\r\n Installed: 1.2.6\r\n Candidate: 1.2.12~ubuntu16.04.1\r\n Version table:\r\n 1.2.12~ubuntu16.04.1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages\r\n 1.2.10ubuntu1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages\r\n *** 1.2.6 100\r\n 100 /var/lib/dpkg/status\r\napt-utils:\r\n Installed: 1.2.6\r\n Candidate: 1.2.12~ubuntu16.04.1\r\n Version table:\r\n 1.2.12~ubuntu16.04.1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages\r\n 1.2.10ubuntu1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages\r\n *** 1.2.6 100\r\n 100 /var/lib/dpkg/status\r\nbase-files:\r\n Installed: 9.4ubuntu3\r\n Candidate: 9.4ubuntu4.2\r\n Version table:\r\n 9.4ubuntu4.2 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages\r\n 9.4ubuntu4 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages\r\n *** 9.4ubuntu3 100\r\n 100 /var/lib/dpkg/status\r\n", map[string]string{ "apt": "apt:\r\n Installed: 1.2.6\r\n Candidate: 1.2.12~ubuntu16.04.1\r\n Version table:\r\n 1.2.12~ubuntu16.04.1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages\r\n 1.2.10ubuntu1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages\r\n *** 1.2.6 100\r\n 100 /var/lib/dpkg/status\r\n", "apt-utils": "apt-utils:\r\n Installed: 1.2.6\r\n Candidate: 1.2.12~ubuntu16.04.1\r\n Version table:\r\n 1.2.12~ubuntu16.04.1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages\r\n 1.2.10ubuntu1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages\r\n *** 1.2.6 100\r\n 100 /var/lib/dpkg/status\r\n", "base-files": "base-files:\r\n Installed: 9.4ubuntu3\r\n Candidate: 9.4ubuntu4.2\r\n Version table:\r\n 9.4ubuntu4.2 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages\r\n 9.4ubuntu4 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages\r\n *** 9.4ubuntu3 100\r\n 100 /var/lib/dpkg/status\r\n", }, }, } d := newDebian(config.ServerInfo{}) for _, tt := range tests { actual := d.splitAptCachePolicy(tt.stdout) if !reflect.DeepEqual(tt.expected, actual) { e := pp.Sprintf("%v", tt.expected) a := pp.Sprintf("%v", actual) t.Errorf("expected %s, actual %s", e, a) } } }
func TestMergeNewVersion(t *testing.T) { var test = struct { a PackageInfoList b PackageInfoList expected PackageInfoList }{ PackageInfoList{ { Name: "hoge", }, }, PackageInfoList{ { Name: "hoge", NewVersion: "1.0.0", NewRelease: "release1", }, }, PackageInfoList{ { Name: "hoge", NewVersion: "1.0.0", NewRelease: "release1", }, }, } test.a.MergeNewVersion(test.b) if !reflect.DeepEqual(test.a, test.expected) { e := pp.Sprintf("%v", test.a) a := pp.Sprintf("%v", test.expected) t.Errorf("expected %s, actual %s", e, a) } }
func TestParsePkgVersion(t *testing.T) { var tests = []struct { in string expected []models.PackageInfo }{ { `Updating FreeBSD repository catalogue... FreeBSD repository is up-to-date. All repositories are up-to-date. bash-4.2.45 < needs updating (remote has 4.3.42_1) gettext-0.18.3.1 < needs updating (remote has 0.19.7) tcl84-8.4.20_2,1 = up-to-date with remote teTeX-base-3.0_25 ? orphaned: print/teTeX-base`, []models.PackageInfo{ { Name: "bash", Version: "4.2.45", NewVersion: "4.3.42_1", }, { Name: "gettext", Version: "0.18.3.1", NewVersion: "0.19.7", }, { Name: "tcl84", Version: "8.4.20_2,1", }, { Name: "teTeX-base", Version: "3.0_25", }, }, }, } d := newBsd(config.ServerInfo{}) for _, tt := range tests { actual := d.parsePkgVersion(tt.in) if !reflect.DeepEqual(tt.expected, actual) { e := pp.Sprintf("%v", tt.expected) a := pp.Sprintf("%v", actual) t.Errorf("expected %s, actual %s", e, a) } } }
func TestParseYumUpdateinfoHeader(t *testing.T) { r := newRedhat(config.ServerInfo{}) var tests = []struct { in string out []models.PackageInfo }{ { " nodejs-0.10.36-3.el6,libuv-0.10.34-1.el6,v8-3.14.5.10-17.el6 ", []models.PackageInfo{ { Name: "nodejs", Version: "0.10.36", Release: "3.el6", }, { Name: "libuv", Version: "0.10.34", Release: "1.el6", }, { Name: "v8", Version: "3.14.5.10", Release: "17.el6", }, }, }, } for _, tt := range tests { if a, err := r.parseYumUpdateinfoHeaderCentOS(tt.in); err != nil { t.Errorf("err: %s", err) } else { if !reflect.DeepEqual(a, tt.out) { e := pp.Sprintf("%#v", tt.out) a := pp.Sprintf("%#v", a) t.Errorf("expected %s, actual %s", e, a) } } } }
func TestParseYumUpdateinfoAmazonLinuxHeader(t *testing.T) { r := newRedhat(config.ServerInfo{}) var tests = []struct { in string out models.DistroAdvisory }{ { "Amazon Linux AMI 2014.03 - ALAS-2015-598: low priority package update for grep", models.DistroAdvisory{ AdvisoryID: "ALAS-2015-598", Severity: "low", }, }, } for _, tt := range tests { a, _, _ := r.parseYumUpdateinfoHeaderAmazon(tt.in) if !reflect.DeepEqual(a, tt.out) { e := pp.Sprintf("%v", tt.out) a := pp.Sprintf("%v", a) t.Errorf("expected %s, actual %s", e, a) } } }
func TestParseYumUpdateinfoListAvailable(t *testing.T) { r := newRedhat(config.ServerInfo{}) rhelStdout := `RHSA-2015:2315 Moderate/Sec. NetworkManager-1:1.0.6-27.el7.x86_64 RHSA-2015:2315 Moderate/Sec. NetworkManager-config-server-1:1.0.6-27.el7.x86_64 RHSA-2015:1705 Important/Sec. bind-libs-lite-32:9.9.4-18.el7_1.5.x86_64 RHSA-2016:0176 Critical/Sec. glibc-2.17-106.el7_2.4.x86_64 RHSA-2015:2401 Low/Sec. grub2-1:2.02-0.29.el7.x86_64 RHSA-2015:2401 Low/Sec. grub2-tools-1:2.02-0.29.el7.x86_64 updateinfo list done` var tests = []struct { in string out []advisoryIDPacks }{ { rhelStdout, []advisoryIDPacks{ { AdvisoryID: "RHSA-2015:2315", PackNames: []string{ "NetworkManager", "NetworkManager-config-server", }, }, { AdvisoryID: "RHSA-2015:1705", PackNames: []string{ "bind-libs-lite", }, }, { AdvisoryID: "RHSA-2016:0176", PackNames: []string{ "glibc", }, }, { AdvisoryID: "RHSA-2015:2401", PackNames: []string{ "grub2", "grub2-tools", }, }, }, }, } for _, tt := range tests { actual, err := r.parseYumUpdateinfoListAvailable(tt.in) if err != nil { t.Errorf("Error has occurred: %s", err) return } for i := range actual { if !reflect.DeepEqual(actual[i], tt.out[i]) { e := pp.Sprintf("%v", tt.out) a := pp.Sprintf("%v", actual) t.Errorf("[%d] expected: %s\nactual: %s", i, e, a) } } } }
func TestParseYumCheckUpdateLinesAmazon(t *testing.T) { r := newRedhat(config.ServerInfo{}) r.Family = "amazon" stdout := `Loaded plugins: priorities, update-motd, upgrade-helper 34 package(s) needed for security, out of 71 available bind-libs.x86_64 32:9.8.2-0.37.rc1.45.amzn1 amzn-main java-1.7.0-openjdk.x86_64 1:1.7.0.95-2.6.4.0.65.amzn1 amzn-main if-not-architecture 100-200 amzn-main ` r.Packages = []models.PackageInfo{ { Name: "bind-libs", Version: "32:9.8.0", Release: "0.33.rc1.45.amzn1", }, { Name: "java-1.7.0-openjdk", Version: "1:1.7.0.0", Release: "2.6.4.0.0.amzn1", }, { Name: "if-not-architecture", Version: "10", Release: "20", }, } var tests = []struct { in string out models.PackageInfoList }{ { stdout, models.PackageInfoList{ { Name: "bind-libs", Version: "32:9.8.0", Release: "0.33.rc1.45.amzn1", NewVersion: "32:9.8.2", NewRelease: "0.37.rc1.45.amzn1", }, { Name: "java-1.7.0-openjdk", Version: "1:1.7.0.0", Release: "2.6.4.0.0.amzn1", NewVersion: "1:1.7.0.95", NewRelease: "2.6.4.0.65.amzn1", }, { Name: "if-not-architecture", Version: "10", Release: "20", NewVersion: "100", NewRelease: "200", }, }, }, } for _, tt := range tests { packInfoList, err := r.parseYumCheckUpdateLines(tt.in) if err != nil { t.Errorf("Error has occurred, err: %s\ntt.in: %v", err, tt.in) return } for i, ePackInfo := range tt.out { if !reflect.DeepEqual(ePackInfo, packInfoList[i]) { e := pp.Sprintf("%v", ePackInfo) a := pp.Sprintf("%v", packInfoList[i]) t.Errorf("[%d] expected %s, actual %s", i, e, a) } } } }
func TestParseYumCheckUpdateLines(t *testing.T) { r := newRedhat(config.ServerInfo{}) r.Family = "centos" stdout := `Loaded plugins: changelog, fastestmirror, keys, protect-packages, protectbase, security Loading mirror speeds from cached hostfile * base: mirror.fairway.ne.jp * epel: epel.mirror.srv.co.ge * extras: mirror.fairway.ne.jp * updates: mirror.fairway.ne.jp 0 packages excluded due to repository protections audit-libs.x86_64 2.3.7-5.el6 base bash.x86_64 4.1.2-33.el6_7.1 updates Obsoleting Packages python-libs.i686 2.6.6-64.el6 rhui-REGION-rhel-server-releases python-ordereddict.noarch 1.1-3.el6ev installed ` r.Packages = []models.PackageInfo{ { Name: "audit-libs", Version: "2.3.6", Release: "4.el6", }, { Name: "bash", Version: "4.1.1", Release: "33", }, { Name: "python-libs", Version: "2.6.0", Release: "1.1-0", }, { Name: "python-ordereddict", Version: "1.0", Release: "1", }, } var tests = []struct { in string out models.PackageInfoList }{ { stdout, models.PackageInfoList{ { Name: "audit-libs", Version: "2.3.6", Release: "4.el6", NewVersion: "2.3.7", NewRelease: "5.el6", }, { Name: "bash", Version: "4.1.1", Release: "33", NewVersion: "4.1.2", NewRelease: "33.el6_7.1", }, { Name: "python-libs", Version: "2.6.0", Release: "1.1-0", NewVersion: "2.6.6", NewRelease: "64.el6", }, { Name: "python-ordereddict", Version: "1.0", Release: "1", NewVersion: "1.1", NewRelease: "3.el6ev", }, }, }, } for _, tt := range tests { packInfoList, err := r.parseYumCheckUpdateLines(tt.in) if err != nil { t.Errorf("Error has occurred, err: %s\ntt.in: %v", err, tt.in) return } for i, ePackInfo := range tt.out { if !reflect.DeepEqual(ePackInfo, packInfoList[i]) { e := pp.Sprintf("%v", ePackInfo) a := pp.Sprintf("%v", packInfoList[i]) t.Errorf("[%d] expected %s, actual %s", i, e, a) } } } }
func TestParseYumUpdateinfoRHEL(t *testing.T) { stdout := `=============================================================================== Important: bind security update =============================================================================== Update ID : RHSA-2015:1705 Release : Type : security Status : final Issued : 2015-09-03 00:00:00 Bugs : 1259087 - CVE-2015-5722 bind: malformed DNSSEC key failed assertion denial of service CVEs : CVE-2015-5722 Description : The Berkeley Internet Name Domain (BIND) is an implementation of : the Domain Name System (DNS) protocols. BIND : includes a DNS server (named); a resolver library : (routines for applications to use when interfacing : with DNS); and tools for verifying that the DNS : server is operating correctly. : Severity : Important =============================================================================== Important: bind security update =============================================================================== Update ID : RHSA-2015:2655 Release : Type : security Status : final Issued : 2015-09-03 01:00:00 Updated : 2015-09-04 00:00:00 Bugs : 1291176 - CVE-2015-8000 bind: responses with a malformed class attribute can trigger an assertion failure in db.c CVEs : CVE-2015-8000 : CVE-2015-8001 Description : The Berkeley Internet Name Domain (BIND) is an implementation of : the Domain Name System (DNS) protocols. BIND : includes a DNS server (named); a resolver library : (routines for applications to use when interfacing : with DNS); and tools for verifying that the DNS : server is operating correctly. : Severity : Low =============================================================================== Moderate: bind security update =============================================================================== Update ID : RHSA-2016:0073 Release : Type : security Status : final Issued : 2015-09-03 02:00:00 Bugs : 1299364 - CVE-2015-8704 bind: specific APL data could trigger an INSIST in apl_42.c CVEs : CVE-2015-8704 : CVE-2015-8705 Description : The Berkeley Internet Name Domain (BIND) is an implementation of : the Domain Name System (DNS) protocols. BIND : includes a DNS server (named); a resolver library : (routines for applications to use when interfacing : with DNS); and tools for verifying that the DNS : server is operating correctly. : Severity : Moderate ` issued, _ := time.Parse("2006-01-02", "2015-09-03") updated, _ := time.Parse("2006-01-02", "2015-09-04") r := newRedhat(config.ServerInfo{}) r.Family = "redhat" var tests = []struct { in string out []distroAdvisoryCveIDs }{ { stdout, []distroAdvisoryCveIDs{ { DistroAdvisory: models.DistroAdvisory{ AdvisoryID: "RHSA-2015:1705", Severity: "Important", Issued: issued, }, CveIDs: []string{"CVE-2015-5722"}, }, { DistroAdvisory: models.DistroAdvisory{ AdvisoryID: "RHSA-2015:2655", Severity: "Low", Issued: issued, Updated: updated, }, CveIDs: []string{ "CVE-2015-8000", "CVE-2015-8001", }, }, { DistroAdvisory: models.DistroAdvisory{ AdvisoryID: "RHSA-2016:0073", Severity: "Moderate", Issued: issued, Updated: updated, }, CveIDs: []string{ "CVE-2015-8704", "CVE-2015-8705", }, }, }, }, } for _, tt := range tests { actual, _ := r.parseYumUpdateinfo(tt.in) for i, advisoryCveIDs := range actual { if !reflect.DeepEqual(tt.out[i], advisoryCveIDs) { e := pp.Sprintf("%v", tt.out[i]) a := pp.Sprintf("%v", advisoryCveIDs) t.Errorf("[%d] Alas is not same. \nexpected: %s\nactual: %s", i, e, a) } } } }
func TestParseAptCachePolicy(t *testing.T) { var tests = []struct { stdout string name string expected packCandidateVer }{ { // Ubuntu 16.04 `openssl: Installed: 1.0.2f-2ubuntu1 Candidate: 1.0.2g-1ubuntu2 Version table: 1.0.2g-1ubuntu2 500 500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages *** 1.0.2f-2ubuntu1 100 100 /var/lib/dpkg/status`, "openssl", packCandidateVer{ Name: "openssl", Installed: "1.0.2f-2ubuntu1", Candidate: "1.0.2g-1ubuntu2", }, }, { // Ubuntu 14.04 `openssl: Installed: 1.0.1f-1ubuntu2.16 Candidate: 1.0.1f-1ubuntu2.17 Version table: 1.0.1f-1ubuntu2.17 0 500 http://archive.ubuntu.com/ubuntu/ trusty-updates/main amd64 Packages 500 http://archive.ubuntu.com/ubuntu/ trusty-security/main amd64 Packages *** 1.0.1f-1ubuntu2.16 0 100 /var/lib/dpkg/status 1.0.1f-1ubuntu2 0 500 http://archive.ubuntu.com/ubuntu/ trusty/main amd64 Packages`, "openssl", packCandidateVer{ Name: "openssl", Installed: "1.0.1f-1ubuntu2.16", Candidate: "1.0.1f-1ubuntu2.17", }, }, { // Ubuntu 12.04 `openssl: Installed: 1.0.1-4ubuntu5.33 Candidate: 1.0.1-4ubuntu5.34 Version table: 1.0.1-4ubuntu5.34 0 500 http://archive.ubuntu.com/ubuntu/ precise-updates/main amd64 Packages 500 http://archive.ubuntu.com/ubuntu/ precise-security/main amd64 Packages *** 1.0.1-4ubuntu5.33 0 100 /var/lib/dpkg/status 1.0.1-4ubuntu3 0 500 http://archive.ubuntu.com/ubuntu/ precise/main amd64 Packages`, "openssl", packCandidateVer{ Name: "openssl", Installed: "1.0.1-4ubuntu5.33", Candidate: "1.0.1-4ubuntu5.34", }, }, } d := newDebian(config.ServerInfo{}) for _, tt := range tests { actual, err := d.parseAptCachePolicy(tt.stdout, tt.name) if err != nil { t.Errorf("Error has occurred: %s, actual: %#v", err, actual) } if !reflect.DeepEqual(tt.expected, actual) { e := pp.Sprintf("%v", tt.expected) a := pp.Sprintf("%v", actual) t.Errorf("expected %s, actual %s", e, a) } } }
func TestGetCveIDParsingChangelog(t *testing.T) { var tests = []struct { in []string expected []string }{ { // verubuntu1 []string{ "systemd", "228-4ubuntu1", `systemd (229-2) unstable; urgency=medium systemd (229-1) unstable; urgency=medium systemd (228-6) unstable; urgency=medium CVE-2015-2325: heap buffer overflow in compile_branch(). (Closes: #781795) CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285) CVE-2015-3210: heap buffer overflow in pcre_compile2() / systemd (228-5) unstable; urgency=medium systemd (228-4) unstable; urgency=medium systemd (228-3) unstable; urgency=medium systemd (228-2) unstable; urgency=medium systemd (228-1) unstable; urgency=medium systemd (227-3) unstable; urgency=medium systemd (227-2) unstable; urgency=medium systemd (227-1) unstable; urgency=medium`, }, []string{ "CVE-2015-2325", "CVE-2015-2326", "CVE-2015-3210", }, }, { // ver []string{ "libpcre3", "2:8.35-7.1ubuntu1", `pcre3 (2:8.38-2) unstable; urgency=low pcre3 (2:8.38-1) unstable; urgency=low pcre3 (2:8.35-8) unstable; urgency=low pcre3 (2:8.35-7.4) unstable; urgency=medium pcre3 (2:8.35-7.3) unstable; urgency=medium pcre3 (2:8.35-7.2) unstable; urgency=low CVE-2015-2325: heap buffer overflow in compile_branch(). (Closes: #781795) CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285) CVE-2015-3210: heap buffer overflow in pcre_compile2() / pcre3 (2:8.35-7.1) unstable; urgency=medium pcre3 (2:8.35-7) unstable; urgency=medium`, }, []string{ "CVE-2015-2325", "CVE-2015-2326", "CVE-2015-3210", }, }, { // ver-ubuntu3 []string{ "sysvinit", "2.88dsf-59.2ubuntu3", `sysvinit (2.88dsf-59.3ubuntu1) xenial; urgency=low sysvinit (2.88dsf-59.3) unstable; urgency=medium CVE-2015-2325: heap buffer overflow in compile_branch(). (Closes: #781795) CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285) CVE-2015-3210: heap buffer overflow in pcre_compile2() / sysvinit (2.88dsf-59.2ubuntu3) xenial; urgency=medium sysvinit (2.88dsf-59.2ubuntu2) wily; urgency=medium sysvinit (2.88dsf-59.2ubuntu1) wily; urgency=medium CVE-2015-2321: heap buffer overflow in pcre_compile2(). (Closes: #783285) sysvinit (2.88dsf-59.2) unstable; urgency=medium sysvinit (2.88dsf-59.1ubuntu3) wily; urgency=medium CVE-2015-2322: heap buffer overflow in pcre_compile2(). (Closes: #783285) sysvinit (2.88dsf-59.1ubuntu2) wily; urgency=medium sysvinit (2.88dsf-59.1ubuntu1) wily; urgency=medium sysvinit (2.88dsf-59.1) unstable; urgency=medium CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285) sysvinit (2.88dsf-59) unstable; urgency=medium sysvinit (2.88dsf-58) unstable; urgency=low sysvinit (2.88dsf-57) unstable; urgency=low`, }, []string{ "CVE-2015-2325", "CVE-2015-2326", "CVE-2015-3210", }, }, { // 1:ver-ubuntu3 []string{ "bsdutils", "1:2.27.1-1ubuntu3", ` util-linux (2.27.1-3ubuntu1) xenial; urgency=medium util-linux (2.27.1-3) unstable; urgency=medium CVE-2015-2325: heap buffer overflow in compile_branch(). (Closes: #781795) CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285) CVE-2015-3210: CVE-2016-1000000heap buffer overflow in pcre_compile2() / util-linux (2.27.1-2) unstable; urgency=medium util-linux (2.27.1-1ubuntu4) xenial; urgency=medium util-linux (2.27.1-1ubuntu3) xenial; urgency=medium util-linux (2.27.1-1ubuntu2) xenial; urgency=medium util-linux (2.27.1-1ubuntu1) xenial; urgency=medium util-linux (2.27.1-1) unstable; urgency=medium util-linux (2.27-3ubuntu1) xenial; urgency=medium util-linux (2.27-3) unstable; urgency=medium util-linux (2.27-2) unstable; urgency=medium util-linux (2.27-1) unstable; urgency=medium util-linux (2.27~rc2-2) experimental; urgency=medium util-linux (2.27~rc2-1) experimental; urgency=medium util-linux (2.27~rc1-1) experimental; urgency=medium util-linux (2.26.2-9) unstable; urgency=medium util-linux (2.26.2-8) experimental; urgency=medium util-linux (2.26.2-7) experimental; urgency=medium util-linux (2.26.2-6ubuntu3) wily; urgency=medium CVE-2015-2329: heap buffer overflow in compile_branch(). (Closes: #781795) util-linux (2.26.2-6ubuntu2) wily; urgency=medium util-linux (2.26.2-6ubuntu1) wily; urgency=medium util-linux (2.26.2-6) unstable; urgency=medium`, }, []string{ "CVE-2015-2325", "CVE-2015-2326", "CVE-2015-3210", "CVE-2016-1000000", }, }, } d := newDebian(config.ServerInfo{}) for _, tt := range tests { actual := d.getCveIDFromChangelog(tt.in[2], tt.in[0], tt.in[1]) if len(actual) != len(tt.expected) { t.Errorf("Len of return array are'nt same. expected %#v, actual %#v", tt.expected, actual) t.Errorf(pp.Sprintf("%s", tt.in)) continue } for i := range tt.expected { if actual[i] != tt.expected[i] { t.Errorf("expected %s, actual %s", tt.expected[i], actual[i]) } } } }
// Scaning unsecure packages using yum-plugin-security. //TODO return whether already expired. func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (CvePacksList, error) { if o.Family == "centos" { // CentOS has no security channel. // So use yum check-update && parse changelog return CvePacksList{}, fmt.Errorf( "yum updateinfo is not suppported on CentOS") } cmd := "yum --color=never repolist" r := o.ssh(util.PrependProxyEnv(cmd), sudo) if !r.isSuccess() { return nil, fmt.Errorf( "Failed to %s. status: %d, stdout: %s, stderr: %s", cmd, r.ExitStatus, r.Stdout, r.Stderr) } // get advisoryID(RHSA, ALAS) - package name,version cmd = "yum --color=never updateinfo list available --security" r = o.ssh(util.PrependProxyEnv(cmd), sudo) if !r.isSuccess() { return nil, fmt.Errorf( "Failed to %s. status: %d, stdout: %s, stderr: %s", cmd, r.ExitStatus, r.Stdout, r.Stderr) } advIDPackNamesList, err := o.parseYumUpdateinfoListAvailable(r.Stdout) // get package name, version, rel to be upgrade. // cmd = "yum check-update --security" cmd = "LANG=en_US.UTF-8 yum --color=never check-update" r = o.ssh(util.PrependProxyEnv(cmd), sudo) if !r.isSuccess(0, 100) { //returns an exit code of 100 if there are available updates. return nil, fmt.Errorf( "Failed to %s. status: %d, stdout: %s, stderr: %s", cmd, r.ExitStatus, r.Stdout, r.Stderr) } updatable, err := o.parseYumCheckUpdateLines(r.Stdout) if err != nil { return nil, fmt.Errorf("Failed to parse %s. err: %s", cmd, err) } o.log.Debugf("%s", pp.Sprintf("%v", updatable)) dict := map[string][]models.PackageInfo{} for _, advIDPackNames := range advIDPackNamesList { packInfoList := models.PackageInfoList{} for _, packName := range advIDPackNames.PackNames { packInfo, found := updatable.FindByName(packName) if !found { return nil, fmt.Errorf( "PackInfo not found. packInfo: %#v", packName) } packInfoList = append(packInfoList, packInfo) continue } dict[advIDPackNames.AdvisoryID] = packInfoList } // get advisoryID(RHSA, ALAS) - CVE IDs cmd = "yum --color=never updateinfo --security update" r = o.ssh(util.PrependProxyEnv(cmd), sudo) if !r.isSuccess() { return nil, fmt.Errorf( "Failed to %s. status: %d, stdout: %s, stderr: %s", cmd, r.ExitStatus, r.Stdout, r.Stderr) } advisoryCveIDsList, err := o.parseYumUpdateinfo(r.Stdout) if err != nil { return CvePacksList{}, err } // pp.Println(advisoryCveIDsList) // All information collected. // Convert to CvePacksList. o.log.Info("Fetching CVE details...") result := CvePacksList{} for _, advIDCveIDs := range advisoryCveIDsList { cveDetails, err := cveapi.CveClient.FetchCveDetails(advIDCveIDs.CveIDs) if err != nil { return nil, err } for _, cveDetail := range cveDetails { found := false for i, p := range result { if cveDetail.CveID == p.CveID { advAppended := append(p.DistroAdvisories, advIDCveIDs.DistroAdvisory) result[i].DistroAdvisories = advAppended packs := dict[advIDCveIDs.DistroAdvisory.AdvisoryID] result[i].Packs = append(result[i].Packs, packs...) found = true break } } if !found { cpinfo := CvePacksInfo{ CveID: cveDetail.CveID, CveDetail: cveDetail, DistroAdvisories: []models.DistroAdvisory{advIDCveIDs.DistroAdvisory}, Packs: dict[advIDCveIDs.DistroAdvisory.AdvisoryID], } result = append(result, cpinfo) } } } o.log.Info("Done") return result, nil }
//TODO return whether already expired. func (o *redhat) scanUnsecurePackagesUsingYumCheckUpdate() (CvePacksList, error) { cmd := "LANG=en_US.UTF-8 yum --color=never check-update" r := o.ssh(util.PrependProxyEnv(cmd), sudo) if !r.isSuccess(0, 100) { //returns an exit code of 100 if there are available updates. return nil, fmt.Errorf( "Failed to %s. status: %d, stdout: %s, stderr: %s", cmd, r.ExitStatus, r.Stdout, r.Stderr) } // get Updateble package name, installed, candidate version. packInfoList, err := o.parseYumCheckUpdateLines(r.Stdout) if err != nil { return nil, fmt.Errorf("Failed to parse %s. err: %s", cmd, err) } o.log.Debugf("%s", pp.Sprintf("%v", packInfoList)) // Collect CVE-IDs in changelog type PackInfoCveIDs struct { PackInfo models.PackageInfo CveIDs []string } var results []PackInfoCveIDs for i, packInfo := range packInfoList { changelog, err := o.getChangelog(packInfo.Name) if err != nil { o.log.Errorf("Failed to collect CVE IDs. err: %s", err) return nil, err } // Collect unique set of CVE-ID in each changelog uniqueCveIDMap := make(map[string]bool) lines := strings.Split(changelog, "\n") for _, line := range lines { cveIDs := o.parseYumUpdateinfoLineToGetCveIDs(line) for _, c := range cveIDs { uniqueCveIDMap[c] = true } } // keys var cveIDs []string for k := range uniqueCveIDMap { cveIDs = append(cveIDs, k) } p := PackInfoCveIDs{ PackInfo: packInfo, CveIDs: cveIDs, } results = append(results, p) o.log.Infof("(%d/%d) Scanned %s-%s-%s -> %s-%s : %s", i+1, len(packInfoList), p.PackInfo.Name, p.PackInfo.Version, p.PackInfo.Release, p.PackInfo.NewVersion, p.PackInfo.NewRelease, p.CveIDs) } // transform datastructure // - From // [ // { // PackInfo: models.PackageInfo, // CveIDs: []string, // }, // ] // - To // map { // CveID: []models.PackageInfo // } cveIDPackInfoMap := make(map[string][]models.PackageInfo) for _, res := range results { for _, cveID := range res.CveIDs { // packInfo, found := o.Packages.FindByName(res.Packname) // if !found { // return CvePacksList{}, fmt.Errorf( // "Faild to transform data structure: %v", res.Packname) // } cveIDPackInfoMap[cveID] = append(cveIDPackInfoMap[cveID], res.PackInfo) } } var uniqueCveIDs []string for cveID := range cveIDPackInfoMap { uniqueCveIDs = append(uniqueCveIDs, cveID) } // cveIDs => []cve.CveInfo o.log.Info("Fetching CVE details...") cveDetails, err := cveapi.CveClient.FetchCveDetails(uniqueCveIDs) if err != nil { return nil, err } o.log.Info("Done") cvePacksList := []CvePacksInfo{} for _, detail := range cveDetails { // Amazon, RHEL do not use this method, so VendorAdvisory do not set. cvePacksList = append(cvePacksList, CvePacksInfo{ CveID: detail.CveID, CveDetail: detail, Packs: cveIDPackInfoMap[detail.CveID], // CvssScore: cinfo.CvssScore(conf.Lang), }) } return cvePacksList, nil }
// Load load the configuraiton TOML file specified by path arg. func (c TOMLLoader) Load(pathToToml, keyPass, sudoPass string) (err error) { var conf Config if _, err := toml.DecodeFile(pathToToml, &conf); err != nil { log.Error("Load config failed", err) return err } Conf.Mail = conf.Mail Conf.Slack = conf.Slack d := conf.Default Conf.Default = d servers := make(map[string]ServerInfo) if keyPass != "" { d.KeyPassword = keyPass } if sudoPass != "" { d.Password = sudoPass } i := 0 for name, v := range conf.Servers { if 0 < len(v.KeyPassword) || 0 < len(v.Password) { log.Warn("[Deprecated] password and keypassword in config file are unsecure. Remove them immediately for a security reason. They will be removed in a future release.") } s := ServerInfo{ServerName: name} switch { case v.User != "": s.User = v.User case d.User != "": s.User = d.User default: return fmt.Errorf("%s is invalid. User is empty", name) } // s.Password = sudoPass s.Password = v.Password if s.Password == "" { s.Password = d.Password } s.Host = v.Host if s.Host == "" { return fmt.Errorf("%s is invalid. host is empty", name) } switch { case v.Port != "": s.Port = v.Port case d.Port != "": s.Port = d.Port default: s.Port = "22" } s.KeyPath = v.KeyPath if s.KeyPath == "" { s.KeyPath = d.KeyPath } if s.KeyPath != "" { if _, err := os.Stat(s.KeyPath); err != nil { return fmt.Errorf( "%s is invalid. keypath: %s not exists", name, s.KeyPath) } } // s.KeyPassword = keyPass s.KeyPassword = v.KeyPassword if s.KeyPassword == "" { s.KeyPassword = d.KeyPassword } s.CpeNames = v.CpeNames if len(s.CpeNames) == 0 { s.CpeNames = d.CpeNames } s.Containers = v.Containers if len(s.Containers) == 0 { s.Containers = d.Containers } s.Optional = v.Optional for _, dkv := range d.Optional { found := false for _, kv := range s.Optional { if dkv[0] == kv[0] { found = true break } } if !found { s.Optional = append(s.Optional, dkv) } } s.LogMsgAnsiColor = Colors[i%len(Colors)] i++ servers[name] = s } log.Debug("Config loaded") log.Debugf("%s", pp.Sprintf("%v", servers)) Conf.Servers = servers return }
// Scaning unsecure packages using yum-plugin-security. // Amazon, RHEL func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (models.VulnInfos, error) { if o.Distro.Family == "centos" { // CentOS has no security channel. // So use yum check-update && parse changelog return nil, fmt.Errorf( "yum updateinfo is not suppported on CentOS") } cmd := "yum --color=never repolist" r := o.exec(util.PrependProxyEnv(cmd), o.sudo()) if !r.isSuccess() { return nil, fmt.Errorf("Failed to SSH: %s", r) } // get advisoryID(RHSA, ALAS) - package name,version major, err := (o.Distro.MajorVersion()) if err != nil { return nil, fmt.Errorf("Not implemented yet: %s, err: %s", o.Distro, err) } if o.Distro.Family == "rhel" && major == 5 { cmd = "yum --color=never list-security --security" } else { cmd = "yum --color=never --security updateinfo list updates" } r = o.exec(util.PrependProxyEnv(cmd), o.sudo()) if !r.isSuccess() { return nil, fmt.Errorf("Failed to SSH: %s", r) } advIDPackNamesList, err := o.parseYumUpdateinfoListAvailable(r.Stdout) // get package name, version, rel to be upgrade. // cmd = "yum check-update --security" cmd = "LANGUAGE=en_US.UTF-8 yum --color=never check-update" r = o.exec(util.PrependProxyEnv(cmd), o.sudo()) if !r.isSuccess(0, 100) { //returns an exit code of 100 if there are available updates. return nil, fmt.Errorf("Failed to SSH: %s", r) } updatable, err := o.parseYumCheckUpdateLines(r.Stdout) if err != nil { return nil, fmt.Errorf("Failed to parse %s. err: %s", cmd, err) } o.log.Debugf("%s", pp.Sprintf("%v", updatable)) // set candidate version info o.Packages.MergeNewVersion(updatable) dict := map[string][]models.PackageInfo{} for _, advIDPackNames := range advIDPackNamesList { packInfoList := models.PackageInfoList{} for _, packName := range advIDPackNames.PackNames { packInfo, found := updatable.FindByName(packName) if !found { return nil, fmt.Errorf( "PackInfo not found. packInfo: %#v", packName) } packInfoList = append(packInfoList, packInfo) continue } dict[advIDPackNames.AdvisoryID] = packInfoList } // get advisoryID(RHSA, ALAS) - CVE IDs if o.Distro.Family == "rhel" && major == 5 { cmd = "yum --color=never info-sec" } else { cmd = "yum --color=never --security updateinfo updates" } r = o.exec(util.PrependProxyEnv(cmd), o.sudo()) if !r.isSuccess() { return nil, fmt.Errorf("Failed to SSH: %s", r) } advisoryCveIDsList, err := o.parseYumUpdateinfo(r.Stdout) if err != nil { return nil, err } // pp.Println(advisoryCveIDsList) // All information collected. // Convert to VulnInfos. vinfos := models.VulnInfos{} for _, advIDCveIDs := range advisoryCveIDsList { for _, cveID := range advIDCveIDs.CveIDs { found := false for i, p := range vinfos { if cveID == p.CveID { advAppended := append(p.DistroAdvisories, advIDCveIDs.DistroAdvisory) vinfos[i].DistroAdvisories = advAppended packs := dict[advIDCveIDs.DistroAdvisory.AdvisoryID] vinfos[i].Packages = append(vinfos[i].Packages, packs...) found = true break } } if !found { cpinfo := models.VulnInfo{ CveID: cveID, DistroAdvisories: []models.DistroAdvisory{advIDCveIDs.DistroAdvisory}, Packages: dict[advIDCveIDs.DistroAdvisory.AdvisoryID], } vinfos = append(vinfos, cpinfo) } } } return vinfos, nil }
// For CentOS func (o *redhat) scanUnsecurePackagesUsingYumCheckUpdate() (models.VulnInfos, error) { cmd := "LANGUAGE=en_US.UTF-8 yum --color=never %s check-update" if o.getServerInfo().Enablerepo != "" { cmd = fmt.Sprintf(cmd, "--enablerepo="+o.getServerInfo().Enablerepo) } else { cmd = fmt.Sprintf(cmd, "") } r := o.exec(util.PrependProxyEnv(cmd), sudo) if !r.isSuccess(0, 100) { //returns an exit code of 100 if there are available updates. return nil, fmt.Errorf("Failed to SSH: %s", r) } // get Updateble package name, installed, candidate version. packInfoList, err := o.parseYumCheckUpdateLines(r.Stdout) if err != nil { return nil, fmt.Errorf("Failed to parse %s. err: %s", cmd, err) } o.log.Debugf("%s", pp.Sprintf("%v", packInfoList)) // set candidate version info o.Packages.MergeNewVersion(packInfoList) // Collect CVE-IDs in changelog type PackInfoCveIDs struct { PackInfo models.PackageInfo CveIDs []string } allChangelog, err := o.getAllChangelog(packInfoList) if err != nil { o.log.Errorf("Failed to getAllchangelog. err: %s", err) return nil, err } // { packageName: changelog-lines } var rpm2changelog map[string]*string rpm2changelog, err = o.parseAllChangelog(allChangelog) if err != nil { return nil, fmt.Errorf("Failed to parseAllChangelog. err: %s", err) } var results []PackInfoCveIDs for i, packInfo := range packInfoList { changelog := o.getChangelogCVELines(rpm2changelog, packInfo) // Collect unique set of CVE-ID in each changelog uniqueCveIDMap := make(map[string]bool) lines := strings.Split(changelog, "\n") for _, line := range lines { cveIDs := o.parseYumUpdateinfoLineToGetCveIDs(line) for _, c := range cveIDs { uniqueCveIDMap[c] = true } } // keys var cveIDs []string for k := range uniqueCveIDMap { cveIDs = append(cveIDs, k) } p := PackInfoCveIDs{ PackInfo: packInfo, CveIDs: cveIDs, } results = append(results, p) o.log.Infof("(%d/%d) Scanned %s-%s-%s -> %s-%s : %s", i+1, len(packInfoList), p.PackInfo.Name, p.PackInfo.Version, p.PackInfo.Release, p.PackInfo.NewVersion, p.PackInfo.NewRelease, p.CveIDs) } // transform datastructure // - From // [ // { // PackInfo: models.PackageInfo, // CveIDs: []string, // }, // ] // - To // map { // CveID: []models.PackageInfo // } cveIDPackInfoMap := make(map[string][]models.PackageInfo) for _, res := range results { for _, cveID := range res.CveIDs { cveIDPackInfoMap[cveID] = append( cveIDPackInfoMap[cveID], res.PackInfo) } } vinfos := []models.VulnInfo{} for k, v := range cveIDPackInfoMap { // Amazon, RHEL do not use this method, so VendorAdvisory do not set. vinfos = append(vinfos, models.VulnInfo{ CveID: k, Packages: v, }) } return vinfos, nil }
func TestParseYumUpdateinfoAmazon(t *testing.T) { r := newRedhat(config.ServerInfo{}) r.Family = "amazon" issued, _ := time.Parse("2006-01-02", "2015-12-15") updated, _ := time.Parse("2006-01-02", "2015-12-16") var tests = []struct { in string out []distroAdvisoryCveIDs }{ { `=============================================================================== Amazon Linux AMI 2014.03 - ALAS-2016-644: medium priority package update for python-rsa =============================================================================== Update ID : ALAS-2016-644 Release : Type : security Status : final Issued : 2015-12-15 13:30 CVEs : CVE-2016-1494 Description : Package updates are available for Amazon Linux AMI that fix the : following vulnerabilities: CVE-2016-1494: : 1295869: : CVE-2016-1494 python-rsa: Signature forgery using : Bleichenbacher'06 attack Severity : medium =============================================================================== Amazon Linux AMI 2014.03 - ALAS-2015-614: medium priority package update for openssl =============================================================================== Update ID : ALAS-2015-614 Release : Type : security Status : final Issued : 2015-12-15 10:00 Updated : 2015-12-16 14:15 CVEs : CVE-2015-3194 : CVE-2015-3195 : CVE-2015-3196 Description : Package updates are available for Amazon Linux AMI that fix the : following vulnerabilities: CVE-2015-3196: : 1288326: : CVE-2015-3196 OpenSSL: Race condition handling PSK : identify hint A race condition flaw, leading to a : double free, was found in the way OpenSSL handled : pre-shared keys (PSKs). A remote attacker could : use this flaw to crash a multi-threaded SSL/TLS : client. : Severity : medium`, []distroAdvisoryCveIDs{ { DistroAdvisory: models.DistroAdvisory{ AdvisoryID: "ALAS-2016-644", Severity: "medium", Issued: issued, }, CveIDs: []string{"CVE-2016-1494"}, }, { DistroAdvisory: models.DistroAdvisory{ AdvisoryID: "ALAS-2015-614", Severity: "medium", Issued: issued, Updated: updated, }, CveIDs: []string{ "CVE-2015-3194", "CVE-2015-3195", "CVE-2015-3196", }, }, }, }, } for _, tt := range tests { actual, _ := r.parseYumUpdateinfo(tt.in) for i, advisoryCveIDs := range actual { if !reflect.DeepEqual(tt.out[i], advisoryCveIDs) { e := pp.Sprintf("%v", tt.out[i]) a := pp.Sprintf("%v", advisoryCveIDs) t.Errorf("[%d] Alas is not same. expected %s, actual %s", i, e, a) } } } }
func sshExecNative(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (result sshResult) { logger := getSSHLogger(log...) cmd = decolateCmd(c, cmd, sudo) logger.Debugf("Command: %s", strings.Replace(maskPassword(cmd, c.Password), "\n", "", -1)) var client *ssh.Client var err error client, err = sshConnect(c) defer client.Close() var session *ssh.Session if session, err = client.NewSession(); err != nil { logger.Errorf("Failed to new session. err: %s, c: %s", err, pp.Sprintf("%v", c)) result.ExitStatus = 999 return } defer session.Close() // http://blog.ralch.com/tutorial/golang-ssh-connection/ modes := ssh.TerminalModes{ ssh.ECHO: 0, // disable echoing ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud } if err = session.RequestPty("xterm", 400, 256, modes); err != nil { logger.Errorf("Failed to request for pseudo terminal. err: %s, c: %s", err, pp.Sprintf("%v", c)) result.ExitStatus = 999 return } var stdoutBuf, stderrBuf bytes.Buffer session.Stdout = &stdoutBuf session.Stderr = &stderrBuf if err := session.Run(cmd); err != nil { if exitErr, ok := err.(*ssh.ExitError); ok { result.ExitStatus = exitErr.ExitStatus() } else { result.ExitStatus = 999 } } else { result.ExitStatus = 0 } result.Stdout = stdoutBuf.String() result.Stderr = stderrBuf.String() result.Host = c.Host result.Port = c.Port logger.Debugf( "SSH executed. cmd: %s, err: %#v, status: %d\nstdout: \n%s\nstderr: \n%s", maskPassword(cmd, c.Password), err, result.ExitStatus, result.Stdout, result.Stderr) return }