diff --git a/.gitignore b/.gitignore
index 4bfdd88308ed5366ced3f9ecab74fb8e2194fcbb..1ce4a32300f6fdda29a52da3ce833af39bcdf9fa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,5 @@ vendor
 dist
 .vscode/
 hack/kind.test.yaml
+
+.idea/
\ No newline at end of file
diff --git a/Gopkg.lock b/Gopkg.lock
index e74b52ee56e89b11f44114a25dfea4c6c4de459c..601bce2efa944f3c317c8e2053267d3ee8f1bce2 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -1,6 +1,14 @@
 # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
 
 
+[[projects]]
+  digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec"
+  name = "github.com/davecgh/go-spew"
+  packages = ["spew"]
+  pruneopts = "UT"
+  revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
+  version = "v1.1.1"
+
 [[projects]]
   digest = "1:938a2672d6ebbb7f7bc63eee3e4b9464c16ffcf77ec8913d3edbf32b4e3984dd"
   name = "github.com/fatih/color"
@@ -113,6 +121,14 @@
   pruneopts = "UT"
   revision = "0131db6d737cfbbfb678f8b7d92e55e27ce46224"
 
+[[projects]]
+  digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
+  name = "github.com/pmezard/go-difflib"
+  packages = ["difflib"]
+  pruneopts = "UT"
+  revision = "792786c7400a136282c1664665ae0a8db921c6c2"
+  version = "v1.0.0"
+
 [[projects]]
   digest = "1:1fccaaeae58b2a2f1af4dbf7eee92ff14f222e161d143bfd20082ef664f91216"
   name = "github.com/spf13/afero"
@@ -161,6 +177,25 @@
   revision = "25b30aa063fc18e48662b86996252eabdcf2f0c7"
   version = "v1.0.0"
 
+[[projects]]
+  digest = "1:ac83cf90d08b63ad5f7e020ef480d319ae890c208f8524622a2f3136e2686b02"
+  name = "github.com/stretchr/objx"
+  packages = ["."]
+  pruneopts = "UT"
+  revision = "477a77ecc69700c7cdeb1fa9e129548e1c1c393c"
+  version = "v0.1.1"
+
+[[projects]]
+  digest = "1:0bcc464dabcfad5393daf87c3f8142911d0f6c52569b837e91a1c15e890265f3"
+  name = "github.com/stretchr/testify"
+  packages = [
+    "assert",
+    "mock",
+  ]
+  pruneopts = "UT"
+  revision = "ffdc059bfe9ce6a4e144ba849dbedead332c6053"
+  version = "v1.3.0"
+
 [[projects]]
   digest = "1:c9c0ba9ea00233c41b91e441cfd490f34b129bbfebcb1858979623bd8de07f72"
   name = "golang.org/x/sys"
@@ -210,7 +245,10 @@
     "github.com/jinzhu/gorm/dialects/postgres",
     "github.com/spf13/cobra",
     "github.com/spf13/viper",
+    "github.com/stretchr/testify/assert",
+    "github.com/stretchr/testify/mock",
     "gopkg.in/yaml.v2",
+    "k8s.io/client-go/util/jsonpath",
   ]
   solver-name = "gps-cdcl"
   solver-version = 1
diff --git a/Gopkg.toml b/Gopkg.toml
index c3975756c8110d5fff656125592b1bd1c8123171..4c062d78702938a322bbd0970e2468000d40b26e 100644
--- a/Gopkg.toml
+++ b/Gopkg.toml
@@ -18,6 +18,10 @@
   name = "github.com/spf13/viper"
   version = "1.0.0"
 
+[[constraint]]
+  name = "github.com/stretchr/testify"
+  version = "1.3.0"
+
 [prune]
   go-tests = true
   unused-packages = true
diff --git a/cfg/1.11-json/config.yaml b/cfg/1.11-json/config.yaml
index ce3c054804051ee2132f5633b200cda811e01bfd..1091b13200d74656c2fd6f94101a6403c1818b07 100644
--- a/cfg/1.11-json/config.yaml
+++ b/cfg/1.11-json/config.yaml
@@ -9,10 +9,6 @@ node:
       - "/var/lib/kubelet/kubeconfig"
 
   kubelet:
-    bins:
-      - "hyperkube kubelet"
-      - "kubelet"
-    defaultconf: "/etc/kubernetes/kubelet/kubelet-config.json"
     defaultsvc: "/etc/systemd/system/kubelet.service"
     defaultkubeconfig: "/var/lib/kubelet/kubeconfig"
 
diff --git a/cfg/1.11/config.yaml b/cfg/1.11/config.yaml
index 1d51f6cf2074999039cf63bf10d220ee16cfd0cf..d6bd9bc4924e5051da5e71cef380eb093cd7a33a 100644
--- a/cfg/1.11/config.yaml
+++ b/cfg/1.11/config.yaml
@@ -9,21 +9,25 @@
 
 master:
   apiserver:
+    confs:
+      - /etc/kubernetes/manifests/kube-apiserver.yaml
+      - /etc/kubernetes/manifests/kube-apiserver.manifest
     defaultconf: /etc/kubernetes/manifests/kube-apiserver.yaml
 
   scheduler:
+    confs:
+      - /etc/kubernetes/manifests/kube-scheduler.yaml
+      - /etc/kubernetes/manifests/kube-scheduler.manifest
     defaultconf: /etc/kubernetes/manifests/kube-scheduler.yaml
 
   controllermanager:
+    confs:
+      - /etc/kubernetes/manifests/kube-controller-manager.yaml
+      - /etc/kubernetes/manifests/kube-controller-manager.manifest
     defaultconf: /etc/kubernetes/manifests/kube-controller-manager.yaml
 
   etcd:
+    confs:
+      - /etc/kubernetes/manifests/etcd.yaml
+      - /etc/kubernetes/manifests/etcd.manifest
     defaultconf: /etc/kubernetes/manifests/etcd.yaml
-
-node:
-  kubelet:
-    defaultconf: /etc/kubernetes/kubelet.conf
-    defaultsvc: /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
-
-  proxy:
-    defaultconf: /etc/kubernetes/addons/kube-proxy-daemonset.yaml
diff --git a/cfg/1.13/config.yaml b/cfg/1.13/config.yaml
index 3a63b4d301250c9e5526115eaca4e99f74b60d83..7093d50cafc90828917327c3e719119a072041cb 100644
--- a/cfg/1.13/config.yaml
+++ b/cfg/1.13/config.yaml
@@ -9,21 +9,25 @@
 
 master:
   apiserver:
+    confs:
+      - /etc/kubernetes/manifests/kube-apiserver.yaml
+      - /etc/kubernetes/manifests/kube-apiserver.manifest
     defaultconf: /etc/kubernetes/manifests/kube-apiserver.yaml
 
   scheduler:
+    confs:
+      - /etc/kubernetes/manifests/kube-scheduler.yaml
+      - /etc/kubernetes/manifests/kube-scheduler.manifest
     defaultconf: /etc/kubernetes/manifests/kube-scheduler.yaml
 
   controllermanager:
+    confs:
+      - /etc/kubernetes/manifests/kube-controller-manager.yaml
+      - /etc/kubernetes/manifests/kube-controller-manager.manifest
     defaultconf: /etc/kubernetes/manifests/kube-controller-manager.yaml
 
   etcd:
+    confs:
+      - /etc/kubernetes/manifests/etcd.yaml
+      - /etc/kubernetes/manifests/etcd.manifest
     defaultconf: /etc/kubernetes/manifests/etcd.yaml
-
-node:
-  kubelet:
-    defaultconf: /etc/kubernetes/kubelet.conf
-    defaultsvc: /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
-
-  proxy:
-    defaultconf: /etc/kubernetes/addons/kube-proxy-daemonset.yaml
diff --git a/cfg/1.13/master.yaml b/cfg/1.13/master.yaml
index e2ac93acc56124c844f6cfe0f87f52a6fe29f52a..6a36b9e85daff76efe9a6040ead39324fab40542 100644
--- a/cfg/1.13/master.yaml
+++ b/cfg/1.13/master.yaml
@@ -220,12 +220,15 @@ groups:
     text: "Ensure that the admission control plugin NamespaceLifecycle is set (Scored)"
     audit: "ps -ef | grep $apiserverbin | grep -v grep"
     tests:
+      bin_op: or
       test_items:
       - flag: "--disable-admission-plugins"
         compare:
           op: nothave
           value: "NamespaceLifecycle"
         set: true
+      - flag: "--disable-admission-plugins"
+        set: false
     remediation: |
       Edit the API server pod specification file $apiserverconf
       on the master node and set the --disable-admission-plugins parameter to
diff --git a/cfg/1.8/config.yaml b/cfg/1.8/config.yaml
index 284ff847b4616af602bf07fa1a36aa0549226dbf..9dc66a500e33e885dbf9193cf1db53f8cef80188 100644
--- a/cfg/1.8/config.yaml
+++ b/cfg/1.8/config.yaml
@@ -31,12 +31,3 @@ master:
       - /etc/kubernetes/manifests/etcd.yaml
       - /etc/kubernetes/manifests/etcd.manifest
     defaultconf: /etc/kubernetes/manifests/etcd.yaml
-
-node:
-  kubelet:
-    defaultconf: /var/lib/kubelet/config.yaml
-    defaultsvc: /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
-    defaultkubeconfig: /etc/kubernetes/kubelet.conf
-  
-  proxy:
-    defaultconf: /etc/kubernetes/addons/kube-proxy-daemonset.yaml
diff --git a/cfg/config.yaml b/cfg/config.yaml
index 09fdc093329195a9ec4efd1dfdfdf3fda984b811..1c86219f45fb1a428d106ae515dcfdf3efee41a4 100644
--- a/cfg/config.yaml
+++ b/cfg/config.yaml
@@ -81,6 +81,9 @@ node:
     bins:
       - "hyperkube kubelet"
       - "kubelet"
+    confs:
+      - "/var/lib/kubelet/config.yaml"
+      - "/etc/kubernetes/kubelet/kubelet-config.json"
     defaultconf: "/var/lib/kubelet/config.yaml"
     defaultsvc: "/etc/systemd/system/kubelet.service.d/10-kubeadm.conf"
     defaultkubeconfig: "/etc/kubernetes/kubelet.conf"
@@ -93,6 +96,7 @@ node:
     confs:
       - /etc/kubernetes/proxy
       - /etc/kubernetes/addons/kube-proxy-daemonset.yaml
+    defaultconf: /etc/kubernetes/addons/kube-proxy-daemonset.yaml
     defaultkubeconfig: "/etc/kubernetes/proxy.conf"
 
 federated:
diff --git a/check/check.go b/check/check.go
index 4ace74b7d3819c43745326c46ea859998ce864ef..3c7884fb746ccd1086e426f51e81718499587437 100644
--- a/check/check.go
+++ b/check/check.go
@@ -36,11 +36,11 @@ const (
 	// PASS check passed.
 	PASS State = "PASS"
 	// FAIL check failed.
-	FAIL = "FAIL"
+	FAIL State = "FAIL"
 	// WARN could not carry out check.
-	WARN = "WARN"
+	WARN State = "WARN"
 	// INFO informational message
-	INFO = "INFO"
+	INFO State = "INFO"
 
 	// MASTER a master node
 	MASTER NodeType = "master"
@@ -62,32 +62,49 @@ func handleError(err error, context string) (errmsg string) {
 type Check struct {
 	ID          string      `yaml:"id" json:"test_number"`
 	Text        string      `json:"test_desc"`
-	Audit       string      `json:"omit"`
+	Audit       string      `json:"audit"`
 	Type        string      `json:"type"`
 	Commands    []*exec.Cmd `json:"omit"`
 	Tests       *tests      `json:"omit"`
 	Set         bool        `json:"omit"`
-	Remediation string      `json:"-"`
+	Remediation string      `json:"remediation"`
 	TestInfo    []string    `json:"test_info"`
 	State       `json:"status"`
 	ActualValue string `json:"actual_value"`
 	Scored      bool   `json:"scored"`
 }
 
+// Runner wraps the basic Run method.
+type Runner interface {
+	// Run runs a given check and returns the execution state.
+	Run(c *Check) State
+}
+
+// NewRunner constructs a default Runner.
+func NewRunner() Runner {
+	return &defaultRunner{}
+}
+
+type defaultRunner struct{}
+
+func (r *defaultRunner) Run(c *Check) State {
+	return c.run()
+}
+
 // Run executes the audit commands specified in a check and outputs
 // the results.
-func (c *Check) Run() {
+func (c *Check) run() State {
 
 	// If check type is skip, force result to INFO
 	if c.Type == "skip" {
 		c.State = INFO
-		return
+		return c.State
 	}
 
 	// If check type is manual or the check is not scored, force result to WARN
 	if c.Type == "manual" || !c.Scored {
 		c.State = WARN
-		return
+		return c.State
 	}
 
 	var out bytes.Buffer
@@ -97,7 +114,7 @@ func (c *Check) Run() {
 	for _, cmd := range c.Commands {
 		if !isShellCommand(cmd.Path) {
 			c.State = WARN
-			return
+			return c.State
 		}
 	}
 
@@ -106,7 +123,7 @@ func (c *Check) Run() {
 	if n == 0 {
 		// Likely a warning message.
 		c.State = WARN
-		return
+		return c.State
 	}
 
 	// Each command runs,
@@ -188,6 +205,7 @@ func (c *Check) Run() {
 	if errmsgs != "" {
 		glog.V(2).Info(errmsgs)
 	}
+	return c.State
 }
 
 // textToCommand transforms an input text representation of commands to be
diff --git a/check/check_test.go b/check/check_test.go
index ab74656a14dd9bf3155dea69d23672c9f5fbd3ef..2cf6e3cf0f322bb18b2281206383d6fe1c867cd9 100644
--- a/check/check_test.go
+++ b/check/check_test.go
@@ -1,3 +1,17 @@
+// Copyright © 2017-2019 Aqua Security Software Ltd. <info@aquasec.com>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package check
 
 import (
@@ -21,7 +35,7 @@ func TestCheck_Run(t *testing.T) {
 
 	for _, testCase := range testCases {
 
-		testCase.check.Run()
+		testCase.check.run()
 
 		if testCase.check.State != testCase.Expected {
 			t.Errorf("test failed, expected %s, actual %s\n", testCase.Expected, testCase.check.State)
diff --git a/check/controls.go b/check/controls.go
index f6d4ab9d97b5466c4511021b4b01e8b5cb6e61d3..0a6183c8528f2a6bd09bc4ccc056e6f91707ffb8 100644
--- a/check/controls.go
+++ b/check/controls.go
@@ -17,8 +17,8 @@ package check
 import (
 	"encoding/json"
 	"fmt"
-
-	yaml "gopkg.in/yaml.v2"
+	"github.com/golang/glog"
+	"gopkg.in/yaml.v2"
 )
 
 // Controls holds all controls to check for master nodes.
@@ -50,6 +50,9 @@ type Summary struct {
 	Info int `json:"total_info"`
 }
 
+// Predicate a predicate on the given Group and Check arguments.
+type Predicate func(group *Group, check *Check) bool
+
 // NewControls instantiates a new master Controls object.
 func NewControls(t NodeType, in []byte) (*Controls, error) {
 	c := new(Controls)
@@ -73,76 +76,44 @@ func NewControls(t NodeType, in []byte) (*Controls, error) {
 	return c, nil
 }
 
-// RunGroup runs all checks in a group.
-func (controls *Controls) RunGroup(gids ...string) Summary {
-	g := []*Group{}
+// RunChecks runs the checks with the given Runner. Only checks for which the filter Predicate returns `true` will run.
+func (controls *Controls) RunChecks(runner Runner, filter Predicate) Summary {
+	var g []*Group
+	m := make(map[string]*Group)
 	controls.Summary.Pass, controls.Summary.Fail, controls.Summary.Warn, controls.Info = 0, 0, 0, 0
 
-	// If no groupid is passed run all group checks.
-	if len(gids) == 0 {
-		gids = controls.getAllGroupIDs()
-	}
-
 	for _, group := range controls.Groups {
+		for _, check := range group.Checks {
 
-		for _, gid := range gids {
-			if gid == group.ID {
-				for _, check := range group.Checks {
-					check.Run()
-					check.TestInfo = append(check.TestInfo, check.Remediation)
-					summarize(controls, check)
-					summarizeGroup(group, check)
-				}
-
-				g = append(g, group)
+			if !filter(group, check) {
+				continue
 			}
-		}
-	}
 
-	controls.Groups = g
-	return controls.Summary
-}
+			state := runner.Run(check)
+			check.TestInfo = append(check.TestInfo, check.Remediation)
 
-// RunChecks runs the checks with the supplied IDs.
-func (controls *Controls) RunChecks(ids ...string) Summary {
-	g := []*Group{}
-	m := make(map[string]*Group)
-	controls.Summary.Pass, controls.Summary.Fail, controls.Summary.Warn, controls.Info = 0, 0, 0, 0
+			// Check if we have already added this checks group.
+			if v, ok := m[group.ID]; !ok {
+				// Create a group with same info
+				w := &Group{
+					ID:     group.ID,
+					Text:   group.Text,
+					Checks: []*Check{},
+				}
 
-	// If no groupid is passed run all group checks.
-	if len(ids) == 0 {
-		ids = controls.getAllCheckIDs()
-	}
+				// Add this check to the new group
+				w.Checks = append(w.Checks, check)
+				summarizeGroup(w, state)
 
-	for _, group := range controls.Groups {
-		for _, check := range group.Checks {
-			for _, id := range ids {
-				if id == check.ID {
-					check.Run()
-					check.TestInfo = append(check.TestInfo, check.Remediation)
-					summarize(controls, check)
-
-					// Check if we have already added this checks group.
-					if v, ok := m[group.ID]; !ok {
-						// Create a group with same info
-						w := &Group{
-							ID:     group.ID,
-							Text:   group.Text,
-							Checks: []*Check{},
-						}
-
-						// Add this check to the new group
-						w.Checks = append(w.Checks, check)
-
-						// Add to groups we have visited.
-						m[w.ID] = w
-						g = append(g, w)
-					} else {
-						v.Checks = append(v.Checks, check)
-					}
-
-				}
+				// Add to groups we have visited.
+				m[w.ID] = w
+				g = append(g, w)
+			} else {
+				v.Checks = append(v.Checks, check)
+				summarizeGroup(v, state)
 			}
+
+			summarize(controls, state)
 		}
 	}
 
@@ -155,29 +126,8 @@ func (controls *Controls) JSON() ([]byte, error) {
 	return json.Marshal(controls)
 }
 
-func (controls *Controls) getAllGroupIDs() []string {
-	var ids []string
-
-	for _, group := range controls.Groups {
-		ids = append(ids, group.ID)
-	}
-	return ids
-}
-
-func (controls *Controls) getAllCheckIDs() []string {
-	var ids []string
-
-	for _, group := range controls.Groups {
-		for _, check := range group.Checks {
-			ids = append(ids, check.ID)
-		}
-	}
-	return ids
-
-}
-
-func summarize(controls *Controls, check *Check) {
-	switch check.State {
+func summarize(controls *Controls, state State) {
+	switch state {
 	case PASS:
 		controls.Summary.Pass++
 	case FAIL:
@@ -186,11 +136,13 @@ func summarize(controls *Controls, check *Check) {
 		controls.Summary.Warn++
 	case INFO:
 		controls.Summary.Info++
+	default:
+		glog.Warningf("Unrecognized state %s", state)
 	}
 }
 
-func summarizeGroup(group *Group, check *Check) {
-	switch check.State {
+func summarizeGroup(group *Group, state State) {
+	switch state {
 	case PASS:
 		group.Pass++
 	case FAIL:
@@ -199,5 +151,7 @@ func summarizeGroup(group *Group, check *Check) {
 		group.Warn++
 	case INFO:
 		group.Info++
+	default:
+		glog.Warningf("Unrecognized state %s", state)
 	}
 }
diff --git a/check/controls_test.go b/check/controls_test.go
index 17c62e57dd3d6684916559aa3b08ce4ea2bf8c48..18e92cbb89af77deb0e8d07afca1042245840d21 100644
--- a/check/controls_test.go
+++ b/check/controls_test.go
@@ -1,3 +1,17 @@
+// Copyright © 2017-2019 Aqua Security Software Ltd. <info@aquasec.com>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package check
 
 import (
@@ -6,11 +20,22 @@ import (
 	"path/filepath"
 	"testing"
 
-	yaml "gopkg.in/yaml.v2"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+	"gopkg.in/yaml.v2"
 )
 
 const cfgDir = "../cfg/"
 
+type mockRunner struct {
+	mock.Mock
+}
+
+func (m *mockRunner) Run(c *Check) State {
+	args := m.Called(c)
+	return args.Get(0).(State)
+}
+
 // validate that the files we're shipping are valid YAML
 func TestYamlFiles(t *testing.T) {
 	err := filepath.Walk(cfgDir, func(path string, info os.FileInfo, err error) error {
@@ -38,3 +63,89 @@ func TestYamlFiles(t *testing.T) {
 		t.Fatalf("failure walking cfg dir: %v\n", err)
 	}
 }
+
+func TestNewControls(t *testing.T) {
+
+	t.Run("Should return error when node type is not specified", func(t *testing.T) {
+		// given
+		in := []byte(`
+---
+controls:
+type: # not specified
+groups:
+`)
+		// when
+		_, err := NewControls(MASTER, in)
+		// then
+		assert.EqualError(t, err, "non-master controls file specified")
+	})
+
+	t.Run("Should return error when input YAML is invalid", func(t *testing.T) {
+		// given
+		in := []byte("BOOM")
+		// when
+		_, err := NewControls(MASTER, in)
+		// then
+		assert.EqualError(t, err, "failed to unmarshal YAML: yaml: unmarshal errors:\n  line 1: cannot unmarshal !!str `BOOM` into check.Controls")
+	})
+
+}
+
+func TestControls_RunChecks(t *testing.T) {
+
+	t.Run("Should run checks matching the filter and update summaries", func(t *testing.T) {
+		// given
+		runner := new(mockRunner)
+		// and
+		in := []byte(`
+---
+type: "master"
+groups:
+- id: G1
+  checks:
+    - id: G1/C1
+- id: G2
+  checks:
+    - id: G2/C1
+`)
+		// and
+		controls, _ := NewControls(MASTER, in)
+		// and
+		runner.On("Run", controls.Groups[0].Checks[0]).Return(PASS)
+		runner.On("Run", controls.Groups[1].Checks[0]).Return(FAIL)
+		// and
+		var runAll Predicate = func(group *Group, c *Check) bool {
+			return true
+		}
+		// when
+		controls.RunChecks(runner, runAll)
+		// then
+		assert.Equal(t, 2, len(controls.Groups))
+		// and
+		G1 := controls.Groups[0]
+		assert.Equal(t, "G1", G1.ID)
+		assert.Equal(t, "G1/C1", G1.Checks[0].ID)
+		assertEqualGroupSummary(t, 1, 0, 0, 0, G1)
+		// and
+		G2 := controls.Groups[1]
+		assert.Equal(t, "G2", G2.ID)
+		assert.Equal(t, "G2/C1", G2.Checks[0].ID)
+		assertEqualGroupSummary(t, 0, 1, 0, 0, G2)
+		// and
+		assert.Equal(t, 1, controls.Summary.Pass)
+		assert.Equal(t, 1, controls.Summary.Fail)
+		assert.Equal(t, 0, controls.Summary.Info)
+		assert.Equal(t, 0, controls.Summary.Warn)
+		// and
+		runner.AssertExpectations(t)
+	})
+
+}
+
+func assertEqualGroupSummary(t *testing.T, pass, fail, info, warn int, actual *Group) {
+	t.Helper()
+	assert.Equal(t, pass, actual.Pass)
+	assert.Equal(t, fail, actual.Fail)
+	assert.Equal(t, info, actual.Info)
+	assert.Equal(t, warn, actual.Warn)
+}
diff --git a/cmd/common.go b/cmd/common.go
index ed6e9b5820bf7d030bef82ac056956df8790ef46..de24273cec99e320d576e949a121f0b0f0aaf360 100644
--- a/cmd/common.go
+++ b/cmd/common.go
@@ -19,15 +19,47 @@ import (
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"strings"
 
 	"github.com/aquasecurity/kube-bench/check"
 	"github.com/golang/glog"
 	"github.com/spf13/viper"
 )
 
-var (
-	errmsgs string
-)
+// NewRunFilter constructs a Predicate based on FilterOpts which determines whether tested Checks should be run or not.
+func NewRunFilter(opts FilterOpts) (check.Predicate, error) {
+
+	if opts.CheckList != "" && opts.GroupList != "" {
+		return nil, fmt.Errorf("group option and check option can't be used together")
+	}
+
+	var groupIDs map[string]bool
+	if opts.GroupList != "" {
+		groupIDs = cleanIDs(opts.GroupList)
+	}
+
+	var checkIDs map[string]bool
+	if opts.CheckList != "" {
+		checkIDs = cleanIDs(opts.CheckList)
+	}
+
+	return func(g *check.Group, c *check.Check) bool {
+		var test = true
+		if len(groupIDs) > 0 {
+			_, ok := groupIDs[g.ID]
+			test = test && ok
+		}
+
+		if len(checkIDs) > 0 {
+			_, ok := checkIDs[c.ID]
+			test = test && ok
+		}
+
+		test = test && (opts.Scored && c.Scored || opts.Unscored && !c.Scored)
+
+		return test
+	}, nil
+}
 
 func runChecks(nodetype check.NodeType) {
 	var summary check.Summary
@@ -40,7 +72,7 @@ func runChecks(nodetype check.NodeType) {
 
 	glog.V(1).Info(fmt.Sprintf("Using benchmark file: %s\n", def))
 
-	// Get the set of exectuables and config files we care about on this type of node.
+	// Get the set of executables and config files we care about on this type of node.
 	typeConf := viper.Sub(string(nodetype))
 	binmap, err := getBinaries(typeConf)
 
@@ -65,18 +97,14 @@ func runChecks(nodetype check.NodeType) {
 		exitWithError(fmt.Errorf("error setting up %s controls: %v", nodetype, err))
 	}
 
-	if groupList != "" && checkList == "" {
-		ids := cleanIDs(groupList)
-		summary = controls.RunGroup(ids...)
-	} else if checkList != "" && groupList == "" {
-		ids := cleanIDs(checkList)
-		summary = controls.RunChecks(ids...)
-	} else if checkList != "" && groupList != "" {
-		exitWithError(fmt.Errorf("group option and check option can't be used together"))
-	} else {
-		summary = controls.RunGroup()
+	runner := check.NewRunner()
+	filter, err := NewRunFilter(filterOpts)
+	if err != nil {
+		exitWithError(fmt.Errorf("error setting up run filter: %v", err))
 	}
 
+	summary = controls.RunChecks(runner, filter)
+
 	// if we successfully ran some tests and it's json format, ignore the warnings
 	if (summary.Fail > 0 || summary.Warn > 0 || summary.Pass > 0 || summary.Info > 0) && jsonFmt {
 		out, err := controls.JSON()
@@ -115,6 +143,10 @@ func prettyPrint(r *check.Controls, summary check.Summary) {
 			colorPrint(check.INFO, fmt.Sprintf("%s %s\n", g.ID, g.Text))
 			for _, c := range g.Checks {
 				colorPrint(c.State, fmt.Sprintf("%s %s\n", c.ID, c.Text))
+
+				if includeTestOutput && c.State == check.FAIL && len(c.ActualValue) > 0 {
+					printRawOutput(c.ActualValue)
+				}
 			}
 		}
 
@@ -213,3 +245,9 @@ func isMaster() bool {
 	}
 	return true
 }
+
+func printRawOutput(output string) {
+	for _, row := range strings.Split(output, "\n") {
+		fmt.Println(fmt.Sprintf("\t %s", row))
+	}
+}
diff --git a/cmd/common_test.go b/cmd/common_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..418e76cbdb64b2686a3989e7a66ce336508814fa
--- /dev/null
+++ b/cmd/common_test.go
@@ -0,0 +1,112 @@
+// Copyright © 2017-2019 Aqua Security Software Ltd. <info@aquasec.com>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cmd
+
+import (
+	"github.com/aquasecurity/kube-bench/check"
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+func TestNewRunFilter(t *testing.T) {
+
+	type TestCase struct {
+		Name       string
+		FilterOpts FilterOpts
+		Group      *check.Group
+		Check      *check.Check
+
+		Expected bool
+	}
+
+	testCases := []TestCase{
+		{
+			Name:       "Should return true when scored flag is enabled and check is scored",
+			FilterOpts: FilterOpts{Scored: true, Unscored: false},
+			Group:      &check.Group{},
+			Check:      &check.Check{Scored: true},
+			Expected:   true,
+		},
+		{
+			Name:       "Should return false when scored flag is enabled and check is not scored",
+			FilterOpts: FilterOpts{Scored: true, Unscored: false},
+			Group:      &check.Group{},
+			Check:      &check.Check{Scored: false},
+			Expected:   false,
+		},
+
+		{
+			Name:       "Should return true when unscored flag is enabled and check is not scored",
+			FilterOpts: FilterOpts{Scored: false, Unscored: true},
+			Group:      &check.Group{},
+			Check:      &check.Check{Scored: false},
+			Expected:   true,
+		},
+		{
+			Name:       "Should return false when unscored flag is enabled and check is scored",
+			FilterOpts: FilterOpts{Scored: false, Unscored: true},
+			Group:      &check.Group{},
+			Check:      &check.Check{Scored: true},
+			Expected:   false,
+		},
+
+		{
+			Name:       "Should return true when group flag contains group's ID",
+			FilterOpts: FilterOpts{Scored: true, Unscored: true, GroupList: "G1,G2,G3"},
+			Group:      &check.Group{ID: "G2"},
+			Check:      &check.Check{},
+			Expected:   true,
+		},
+		{
+			Name:       "Should return false when group flag doesn't contain group's ID",
+			FilterOpts: FilterOpts{GroupList: "G1,G3"},
+			Group:      &check.Group{ID: "G2"},
+			Check:      &check.Check{},
+			Expected:   false,
+		},
+
+		{
+			Name:       "Should return true when check flag contains check's ID",
+			FilterOpts: FilterOpts{Scored: true, Unscored: true, CheckList: "C1,C2,C3"},
+			Group:      &check.Group{},
+			Check:      &check.Check{ID: "C2"},
+			Expected:   true,
+		},
+		{
+			Name:       "Should return false when check flag doesn't contain check's ID",
+			FilterOpts: FilterOpts{CheckList: "C1,C3"},
+			Group:      &check.Group{},
+			Check:      &check.Check{ID: "C2"},
+			Expected:   false,
+		},
+	}
+
+	for _, testCase := range testCases {
+		t.Run(testCase.Name, func(t *testing.T) {
+			filter, _ := NewRunFilter(testCase.FilterOpts)
+			assert.Equal(t, testCase.Expected, filter(testCase.Group, testCase.Check))
+		})
+	}
+
+	t.Run("Should return error when both group and check flags are used", func(t *testing.T) {
+		// given
+		opts := FilterOpts{GroupList: "G1", CheckList: "C1"}
+		// when
+		_, err := NewRunFilter(opts)
+		// then
+		assert.EqualError(t, err, "group option and check option can't be used together")
+	})
+
+}
diff --git a/cmd/root.go b/cmd/root.go
index eab044dce825189160cb09452be0d6ab428e9d10..7b09fcdff75db2c3fecf4c7293ce82279ac09abd 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -25,6 +25,13 @@ import (
 	"github.com/spf13/viper"
 )
 
+type FilterOpts struct {
+	CheckList string
+	GroupList string
+	Scored    bool
+	Unscored  bool
+}
+
 var (
 	envVarsPrefix      = "KUBE_BENCH"
 	defaultKubeVersion = "1.6"
@@ -33,14 +40,14 @@ var (
 	cfgDir             string
 	jsonFmt            bool
 	pgSQL              bool
-	checkList          string
-	groupList          string
 	masterFile         = "master.yaml"
 	nodeFile           = "node.yaml"
 	federatedFile      string
 	noResults          bool
 	noSummary          bool
 	noRemediations     bool
+	filterOpts         FilterOpts
+	includeTestOutput bool
 )
 
 // RootCmd represents the base command when called without any subcommands
@@ -79,16 +86,19 @@ func init() {
 	RootCmd.PersistentFlags().BoolVar(&noRemediations, "noremediations", false, "Disable printing of remediations section")
 	RootCmd.PersistentFlags().BoolVar(&jsonFmt, "json", false, "Prints the results as JSON")
 	RootCmd.PersistentFlags().BoolVar(&pgSQL, "pgsql", false, "Save the results to PostgreSQL")
+	RootCmd.PersistentFlags().BoolVar(&filterOpts.Scored, "scored", true, "Run the scored CIS checks")
+	RootCmd.PersistentFlags().BoolVar(&filterOpts.Unscored, "unscored", true, "Run the unscored CIS checks")
+	RootCmd.PersistentFlags().BoolVar(&includeTestOutput, "include-test-output", false, "Prints the actual result when test fails")
 
 	RootCmd.PersistentFlags().StringVarP(
-		&checkList,
+		&filterOpts.CheckList,
 		"check",
 		"c",
 		"",
 		`A comma-delimited list of checks to run as specified in CIS document. Example --check="1.1.1,1.1.2"`,
 	)
 	RootCmd.PersistentFlags().StringVarP(
-		&groupList,
+		&filterOpts.GroupList,
 		"group",
 		"g",
 		"",
diff --git a/cmd/util.go b/cmd/util.go
index 87cda65a27d64171a4e57f2a655f525556d014c5..29b7d697ad1181988ed16f64affc4603d948de41 100644
--- a/cmd/util.go
+++ b/cmd/util.go
@@ -50,15 +50,18 @@ func continueWithError(err error, msg string) string {
 	return ""
 }
 
-func cleanIDs(list string) []string {
+func cleanIDs(list string) map[string]bool {
 	list = strings.Trim(list, ",")
 	ids := strings.Split(list, ",")
 
+	set := make(map[string]bool)
+
 	for _, id := range ids {
 		id = strings.Trim(id, " ")
+		set[id] = true
 	}
 
-	return ids
+	return set
 }
 
 // ps execs out to the ps command; it's separated into a function so we can write tests