| 1 |  |  | #!/usr/bin/python3 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 2 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 3 |  |  | import argparse | 
            
                                                                                                            
                            
            
                                    
            
            
                | 4 |  |  | import subprocess | 
            
                                                                                                            
                            
            
                                    
            
            
                | 5 |  |  | import pathlib | 
            
                                                                                                            
                            
            
                                    
            
            
                | 6 |  |  | import sys | 
            
                                                                                                            
                            
            
                                    
            
            
                | 7 |  |  | import textwrap | 
            
                                                                                                            
                            
            
                                    
            
            
                | 8 |  |  | import os | 
            
                                                                                                            
                            
            
                                    
            
            
                | 9 |  |  | import time | 
            
                                                                                                            
                            
            
                                    
            
            
                | 10 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 11 |  |  | PROG_DESC = (''' Create and test content files for Kubernetes API checks. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 12 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 13 |  |  | This script is intended to help content writers create a new application check | 
            
                                                                                                            
                            
            
                                    
            
            
                | 14 |  |  | for OCP4/Kubernetes. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 15 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 16 |  |  | - The 'create' subcommand creates the initial files for a new rule and fetches | 
            
                                                                                                            
                            
            
                                    
            
            
                | 17 |  |  |   the raw URL of the object in question (unless you specify the URL). | 
            
                                                                                                            
                            
            
                                    
            
            
                | 18 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 19 |  |  | - The 'test' subcommand builds your content locally and tests directly using an | 
            
                                                                                                            
                            
            
                                    
            
            
                | 20 |  |  |   openscap podman container. The scan container will test against yaml files | 
            
                                                                                                            
                            
            
                                    
            
            
                | 21 |  |  |   staged under --objectdir. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 22 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 23 |  |  | - The 'cluster-test' subcommand pushes the content to your cluster, and then | 
            
                                                                                                            
                            
            
                                    
            
            
                | 24 |  |  |   runs a Platform scan for your rule with compliance-operator. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 25 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 26 |  |  | Example workflow: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 27 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 28 |  |  | $ utils/add_platform_rule.py create --rule=ocp_proxy_has_ca \ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 29 |  |  |   --type="proxies.config" --name="cluster" \ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 30 |  |  |   --yamlpath=".spec.trustedCA.name" --match="[a-zA-Z0-9]*" | 
            
                                                                                                            
                            
            
                                    
            
            
                | 31 |  |  | creating check for "/apis/config.openshift.io/v1/proxies/cluster" with yamlpath ".spec.trustedCA.name" satisfying match of "[a-zA-Z0-9]*" | 
            
                                                                                                            
                            
            
                                    
            
            
                | 32 |  |  | wrote applications/openshift/ocp_proxy_has_ca/rule.yml | 
            
                                                                                                            
                            
            
                                    
            
            
                | 33 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 34 |  |  | $ mkdir -p /tmp/apis/config.openshift.io/v1/proxies/ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 35 |  |  | $ oc get proxies.config/cluster -o yaml > /tmp/apis/config.openshift.io/v1/proxies/cluster | 
            
                                                                                                            
                            
            
                                    
            
            
                | 36 |  |  | $ utils/add_platform_rule.py test --rule=ocp_proxy_has_ca | 
            
                                                                                                            
                            
            
                                    
            
            
                | 37 |  |  | testing rule ocp_proxy_has_ca locally | 
            
                                                                                                            
                            
            
                                    
            
            
                | 38 |  |  | Title | 
            
                                                                                                            
                            
            
                                    
            
            
                | 39 |  |  |         None | 
            
                                                                                                            
                            
            
                                    
            
            
                | 40 |  |  | Rule | 
            
                                                                                                            
                            
            
                                    
            
            
                | 41 |  |  |         xccdf_org.ssgproject.content_rule_ocp_proxy_has_ca | 
            
                                                                                                            
                            
            
                                    
            
            
                | 42 |  |  | Ident | 
            
                                                                                                            
                            
            
                                    
            
            
                | 43 |  |  |         CCE-84209-6 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 44 |  |  | Result | 
            
                                                                                                            
                            
            
                                    
            
            
                | 45 |  |  |         pass | 
            
                                                                                                            
                            
            
                                    
            
            
                | 46 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 47 |  |  | $ utils/add_platform_rule.py cluster-test --rule=ocp_proxy_has_ca | 
            
                                                                                                            
                            
            
                                    
            
            
                | 48 |  |  | testing rule ocp_proxy_has_ca in-cluster | 
            
                                                                                                            
                            
            
                                    
            
            
                | 49 |  |  | deploying compliance-operator | 
            
                                                                                                            
                            
            
                                    
            
            
                | 50 |  |  | pushing image build to cluster | 
            
                                                                                                            
                            
            
                                    
            
            
                | 51 |  |  | waiting for cleanup from previous test run | 
            
                                                                                                            
                            
            
                                    
            
            
                | 52 |  |  | output from last phase check: LAUNCHING NOT-AVAILABLE | 
            
                                                                                                            
                            
            
                                    
            
            
                | 53 |  |  | output from last phase check: RUNNING NOT-AVAILABLE | 
            
                                                                                                            
                            
            
                                    
            
            
                | 54 |  |  | output from last phase check: AGGREGATING NOT-AVAILABLE | 
            
                                                                                                            
                            
            
                                    
            
            
                | 55 |  |  | output from last phase check: DONE COMPLIANT | 
            
                                                                                                            
                            
            
                                    
            
            
                | 56 |  |  | COMPLIANT | 
            
                                                                                                            
                            
            
                                    
            
            
                | 57 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 58 |  |  | ''') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 59 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 60 |  |  | PLATFORM_RULE_DIR = 'applications/openshift' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 61 |  |  | OSCAP_TEST_IMAGE = 'quay.io/compliance-operator/openscap-ocp:1.3.4' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 62 |  |  | OSCAP_CMD_OPTS = 'oscap xccdf eval --verbose INFO --fetch-remote-resources --profile xccdf_org.ssgproject.content_profile_test --results-arf /tmp/report-arf.xml /content/ssg-ocp4-ds.xml' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 63 |  |  | PROFILE_PATH = 'ocp4/profiles/test.profile' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 64 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 65 |  |  | MOCK_VERSION = ('''status: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 66 |  |  |   versions: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 67 |  |  |   - name: operator | 
            
                                                                                                            
                            
            
                                    
            
            
                | 68 |  |  |     version: 4.6.0-0.ci-2020-06-15-112708 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 69 |  |  |   - name: openshift-apiserver | 
            
                                                                                                            
                            
            
                                    
            
            
                | 70 |  |  |     version: 4.6.0-0.ci-2020-06-15-112708 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 71 |  |  | ''') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 72 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 73 |  |  | RULE_TEMPLATE = ('''prodtype: ocp4 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 74 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 75 |  |  | title: {TITLE} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 76 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 77 |  |  | description: {DESC} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 78 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 79 |  |  | rationale: TBD | 
            
                                                                                                            
                            
            
                                    
            
            
                | 80 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 81 |  |  | identifiers: {{}} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 82 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 83 |  |  | severity: {SEV} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 84 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 85 |  |  | warnings: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 86 |  |  | - general: |- | 
            
                                                                                                            
                            
            
                                    
            
            
                | 87 |  |  |     {{{{{{ openshift_cluster_setting("{URL}") | indent(4) }}}}}} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 88 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 89 |  |  | template: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 90 |  |  |   name: yamlfile_value | 
            
                                                                                                            
                            
            
                                    
            
            
                | 91 |  |  |   vars: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 92 |  |  |     ocp_data: "true"{ENTITY_CHECK}{CHECK_EXISTENCE} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 93 |  |  |     filepath: {URL} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 94 |  |  |     yamlpath: "{YAMLPATH}" | 
            
                                                                                                            
                            
            
                                    
            
            
                | 95 |  |  |     values: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 96 |  |  |     - value: "{MATCH}"{CHECK_TYPE} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 97 |  |  | ''') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 98 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 99 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 100 |  |  | def operation_value(value): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 101 |  |  |     if value: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 102 |  |  |         return '\n      operation: "pattern match"\n      type: "string"' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 103 |  |  |     else: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 104 |  |  |         return '' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 105 |  |  |  | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 106 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 107 |  |  | def entity_value(value): | 
            
                                                                        
                            
            
                                    
            
            
                | 108 |  |  |     if value is not None: | 
            
                                                                        
                            
            
                                    
            
            
                | 109 |  |  |         return '\n    entity_check: "%s"' % value | 
            
                                                                        
                            
            
                                    
            
            
                | 110 |  |  |     else: | 
            
                                                                        
                            
            
                                    
            
            
                | 111 |  |  |         return '' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 112 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 113 |  |  | def check_existence_value(value): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 114 |  |  |     if value is not None: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 115 |  |  |         return '\n    check_existence: "%s"' % value | 
            
                                                                                                            
                            
            
                                    
            
            
                | 116 |  |  |     else: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 117 |  |  |         return '' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 118 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 119 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 120 |  |  | PROFILE_TEMPLATE = ('''documentation_complete: true | 
            
                                                                                                            
                            
            
                                    
            
            
                | 121 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 122 |  |  | title: 'Test Profile for {RULE_NAME}' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 123 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 124 |  |  | platform: ocp4 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 125 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 126 |  |  | description: Test Profile | 
            
                                                                                                            
                            
            
                                    
            
            
                | 127 |  |  | selections: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 128 |  |  | - {RULE_NAME} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 129 |  |  | ''') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 130 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 131 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 132 |  |  | TEST_SCAN_TEMPLATE = ('''apiVersion: compliance.openshift.io/v1alpha1 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 133 |  |  | kind: ComplianceScan | 
            
                                                                                                            
                            
            
                                    
            
            
                | 134 |  |  | metadata: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 135 |  |  |   name: test | 
            
                                                                                                            
                            
            
                                    
            
            
                | 136 |  |  | spec: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 137 |  |  |   scanType: Platform | 
            
                                                                                                            
                            
            
                                    
            
            
                | 138 |  |  |   profile: {PROFILE} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 139 |  |  |   content: ssg-ocp4-ds.xml | 
            
                                                                                                            
                            
            
                                    
            
            
                | 140 |  |  |   contentImage: image-registry.openshift-image-registry.svc:5000/openshift-compliance/openscap-ocp4-ds:latest | 
            
                                                                                                            
                            
            
                                    
            
            
                | 141 |  |  |   debug: true | 
            
                                                                                                            
                            
            
                                    
            
            
                | 142 |  |  | ''') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 143 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 144 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 145 |  |  | def needs_oc(func): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 146 |  |  |     def wrapper(args): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 147 |  |  |         if which('oc') is None: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 148 |  |  |             print('oc is required for this command.') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 149 |  |  |             return 1 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 150 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 151 |  |  |         return func(args) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 152 |  |  |     return wrapper | 
            
                                                                                                            
                            
            
                                    
            
            
                | 153 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 154 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 155 |  |  | def needs_working_cluster(func): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 156 |  |  |     def wrapper(args): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 157 |  |  |         ret_code, output = subprocess.getstatusoutput( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 158 |  |  |             'oc whoami') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 159 |  |  |         if ret_code != 0: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 160 |  |  |             print("* Error connecting to cluster") | 
            
                                                                                                            
                            
            
                                    
            
            
                | 161 |  |  |             print(output) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 162 |  |  |             return ret_code | 
            
                                                                                                            
                            
            
                                    
            
            
                | 163 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 164 |  |  |         return func(args) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 165 |  |  |     return wrapper | 
            
                                                                                                            
                            
            
                                    
            
            
                | 166 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 167 |  |  | def which(program): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 168 |  |  |     fpath, fname = os.path.split(program) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 169 |  |  |     if fpath: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 170 |  |  |         if os.path.isfile(fpath) and os.access(fpath, os.X_OK): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 171 |  |  |             return program | 
            
                                                                                                            
                            
            
                                    
            
            
                | 172 |  |  |     else: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 173 |  |  |         for path in os.environ["PATH"].split(os.pathsep): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 174 |  |  |             exe_file = os.path.join(path, program) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 175 |  |  |             if os.path.isfile(exe_file) and os.access(exe_file, os.X_OK): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 176 |  |  |                 return exe_file | 
            
                                                                                                            
                            
            
                                    
            
            
                | 177 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 178 |  |  |     return None | 
            
                                                                                                            
                            
            
                                    
            
            
                | 179 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 180 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 181 |  |  | @needs_oc | 
            
                                                                                                            
                            
            
                                    
            
            
                | 182 |  |  | def createFunc(args): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 183 |  |  |     url = args.url | 
            
                                                                                                            
                            
            
                                    
            
            
                | 184 |  |  |     retries = 0 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 185 |  |  |     namespace_flag = '' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 186 |  |  |     if args.namespace is not None: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 187 |  |  |         namespace_flag = '-n ' + args.namespace | 
            
                                                                                                            
                            
            
                                    
            
            
                | 188 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 189 |  |  |     group_path = os.path.join(PLATFORM_RULE_DIR, args.group) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 190 |  |  |     if args.group: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 191 |  |  |         if not os.path.isdir(group_path): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 192 |  |  |             print("ERROR: The specified group '%s' doesn't exist in the '%s' directory" % ( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 193 |  |  |                 args.group, PLATFORM_RULE_DIR)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 194 |  |  |             return 0 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 195 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 196 |  |  |     rule_path = os.path.join(group_path, args.rule) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 197 |  |  |     while url is None and retries < 5: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 198 |  |  |         retries += 1 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 199 |  |  |         ret_code, output = subprocess.getstatusoutput( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 200 |  |  |             'oc get %s/%s -o template="{{.metadata.selfLink}}" %s' % (args.type, args.name, namespace_flag)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 201 |  |  |         if ret_code != 0: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 202 |  |  |             print('error running oc, check connection to the cluster: %d\n %s' % ( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 203 |  |  |                 ret_code, output)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 204 |  |  |             continue | 
            
                                                                                                            
                            
            
                                    
            
            
                | 205 |  |  |         if len(output) > 0 and '/api' in output: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 206 |  |  |             url = output | 
            
                                                                                                            
                            
            
                                    
            
            
                | 207 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 208 |  |  |     if url == None: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 209 |  |  |         print('there was a problem finding the URL from the oc debug output. Hint: override this automatic check with --url') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 210 |  |  |         return 1 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 211 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 212 |  |  |     print('* Creating check for "%s" with yamlpath "%s" satisfying match of "%s"' % ( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 213 |  |  |         url, args.yamlpath, args.match)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 214 |  |  |     rule_yaml_path = os.path.join(rule_path, 'rule.yml') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 215 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 216 |  |  |     pathlib.Path(rule_path).mkdir(parents=True, exist_ok=True) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 217 |  |  |     with open(rule_yaml_path, 'w') as f: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 218 |  |  |         f.write(RULE_TEMPLATE.format(URL=url, TITLE=args.title, SEV=args.severity, IDENT=args.identifiers, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 219 |  |  |                                      DESC=args.description, YAMLPATH=args.yamlpath, MATCH=args.match, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 220 |  |  |                                      NEGATE=str(args.negate).lower(), | 
            
                                                                                                            
                            
            
                                    
            
            
                | 221 |  |  |                                      CHECK_TYPE=operation_value(args.regex), | 
            
                                                                                                            
                            
            
                                    
            
            
                | 222 |  |  |                                      CHECK_EXISTENCE=check_existence_value(args.check_existence), | 
            
                                                                                                            
                            
            
                                    
            
            
                | 223 |  |  |                                      ENTITY_CHECK=entity_value(args.match_entity))) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 224 |  |  |     print('* Wrote ' + rule_yaml_path) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 225 |  |  |     return 0 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 226 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 227 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 228 |  |  | def createTestProfile(rule): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 229 |  |  |     # create a solo profile for rule | 
            
                                                                                                            
                            
            
                                    
            
            
                | 230 |  |  |     with open(PROFILE_PATH, 'w') as f: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 231 |  |  |         f.write(PROFILE_TEMPLATE.format(RULE_NAME=rule)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 232 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 233 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 234 |  |  | @needs_oc | 
            
                                                                                                            
                            
            
                                    
            
            
                | 235 |  |  | @needs_working_cluster | 
            
                                                                                                            
                            
            
                                    
            
            
                | 236 |  |  | def clusterTestFunc(args): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 237 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 238 |  |  |     print('* Testing rule %s in-cluster' % args.rule) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 239 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 240 |  |  |     findout = subprocess.getoutput( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 241 |  |  |         "find %s -name '%s' -type d" % (PLATFORM_RULE_DIR, args.rule)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 242 |  |  |     if findout == "": | 
            
                                                                                                            
                            
            
                                    
            
            
                | 243 |  |  |         print('ERROR: no rule for %s, run "create" first' % args.rule) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 244 |  |  |         return 1 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 245 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 246 |  |  |     if not args.skip_deploy: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 247 |  |  |         subprocess.run("utils/deploy_compliance_operator.sh") | 
            
                                                                                                            
                            
            
                                    
            
            
                | 248 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 249 |  |  |     if not args.skip_build: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 250 |  |  |         createTestProfile(args.rule) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 251 |  |  |         print('* Pushing image build to cluster') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 252 |  |  |         # execute the build_ds_container script | 
            
                                                                                                            
                            
            
                                    
            
            
                | 253 |  |  |         buildp = subprocess.run( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 254 |  |  |             ['utils/build_ds_container.sh', '-P', 'ocp4', '-P', 'rhcos4']) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 255 |  |  |         if buildp.returncode != 0: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 256 |  |  |             try: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 257 |  |  |                 os.remove(PROFILE_PATH) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 258 |  |  |             except OSError: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 259 |  |  |                 pass | 
            
                                                                                                            
                            
            
                                    
            
            
                | 260 |  |  |             return 1 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 261 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 262 |  |  |     ret_code, _ = subprocess.getstatusoutput( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 263 |  |  |         'oc delete compliancescans/test') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 264 |  |  |     if ret_code == 0: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 265 |  |  |         # if previous compliancescans were actually deleted, wait a bit to allow resources to clean up. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 266 |  |  |         print('* Waiting for cleanup from a previous test run') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 267 |  |  |         time.sleep(20) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 268 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 269 |  |  |     # create a single-rule scan | 
            
                                                                                                            
                            
            
                                    
            
            
                | 270 |  |  |     print("* Running scan with rule '%s'" % args.rule) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 271 |  |  |     profile = 'xccdf_org.ssgproject.content_profile_test' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 272 |  |  |     apply_cmd = ['oc', 'apply', '-f', '-'] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 273 |  |  |     with subprocess.Popen(apply_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) as proc: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 274 |  |  |         _, err = proc.communicate( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 275 |  |  |             input=TEST_SCAN_TEMPLATE.format(PROFILE=profile).encode()) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 276 |  |  |         if proc.returncode != 0: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 277 |  |  |             print('Error applying scan object: %s' % err) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 278 |  |  |             try: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 279 |  |  |                 os.remove(PROFILE_PATH) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 280 |  |  |             except OSError: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 281 |  |  |                 pass | 
            
                                                                                                            
                            
            
                                    
            
            
                | 282 |  |  |             return 1 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 283 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 284 |  |  |     # poll for the DONE result | 
            
                                                                                                            
                            
            
                                    
            
            
                | 285 |  |  |     timeout = time.time() + 120   # A couple of minutes is generous for the platform scan. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 286 |  |  |     scan_result = None | 
            
                                                                                                            
                            
            
                                    
            
            
                | 287 |  |  |     while True: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 288 |  |  |         ret_code, output = subprocess.getstatusoutput( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 289 |  |  |             'oc get compliancescans/test -o template="{{.status.phase}} {{.status.result}}"') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 290 |  |  |         if output is not None: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 291 |  |  |             print('> Output from last phase check: %s' % output) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 292 |  |  |         if output.startswith('DONE'): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 293 |  |  |             scan_result = output[5:] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 294 |  |  |             break | 
            
                                                                                                            
                            
            
                                    
            
            
                | 295 |  |  |         if time.time() >= timeout: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 296 |  |  |             break | 
            
                                                                                                            
                            
            
                                    
            
            
                | 297 |  |  |         time.sleep(2) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 298 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 299 |  |  |     if scan_result == None: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 300 |  |  |         print('ERROR: Timeout waiting for scan to finish') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 301 |  |  |         return 1 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 302 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 303 |  |  |     print("* The result is '%s'" % scan_result) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 304 |  |  |     return 0 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 305 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 306 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 307 |  |  | def testFunc(args): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 308 |  |  |     if which('podman') is None: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 309 |  |  |         print('podman is required') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 310 |  |  |         return 1 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 311 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 312 |  |  |     print('testing rule %s locally' % args.rule) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 313 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 314 |  |  |     if not args.skip_build: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 315 |  |  |         createTestProfile(args.rule) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 316 |  |  |         ret_code, out = subprocess.getstatusoutput('./build_product --datastream-only ocp4') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 317 |  |  |         if ret_code != 0: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 318 |  |  |             print('build failed: %s' % out) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 319 |  |  |             return 1 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 320 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 321 |  |  |     # mock a passing result for the implicit ocp4 version check | 
            
                                                                                                            
                            
            
                                    
            
            
                | 322 |  |  |     version_dir = args.objectdir + '/apis/config.openshift.io/v1/clusteroperators' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 323 |  |  |     if not os.path.exists(version_dir): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 324 |  |  |         pathlib.Path(version_dir).mkdir(parents=True, exist_ok=True) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 325 |  |  |         with open(version_dir + '/openshift-apiserver', 'w') as f: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 326 |  |  |             f.write(MOCK_VERSION) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 327 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 328 |  |  |     pod_cmd = 'podman run -it --security-opt label=disable -v "%s:/content" -v "%s:/kubernetes-api-resources" %s %s' % (args.contentdir, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 329 |  |  |                                                                                                                         args.objectdir, OSCAP_TEST_IMAGE, OSCAP_CMD_OPTS) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 330 |  |  |     print(subprocess.getoutput(pod_cmd)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 331 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 332 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 333 |  |  | def main(): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 334 |  |  |     parser = argparse.ArgumentParser( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 335 |  |  |         prog="add_platform_rule.py", | 
            
                                                                                                            
                            
            
                                    
            
            
                | 336 |  |  |         formatter_class=argparse.RawDescriptionHelpFormatter, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 337 |  |  |         description=textwrap.dedent(PROG_DESC)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 338 |  |  |     subparser = parser.add_subparsers( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 339 |  |  |         dest='subcommand', title='subcommands', help='pick one') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 340 |  |  |     create_parser = subparser.add_parser( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 341 |  |  |         'create', help='Bootstrap the XML and YML files under %s for a new check.' % PLATFORM_RULE_DIR) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 342 |  |  |     create_parser.add_argument( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 343 |  |  |         '--rule', required=True, help='The name of the rule to create. Required.') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 344 |  |  |     create_parser.add_argument( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 345 |  |  |         '--group', default="", help='The group directory of the rule to create.') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 346 |  |  |     create_parser.add_argument( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 347 |  |  |         '--name', required=True, help='The name of the Kubernetes object to check. Required.') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 348 |  |  |     create_parser.add_argument( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 349 |  |  |         '--type', required=True, help='The type of Kubernetes object, e.g., configmap. Required.') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 350 |  |  |     create_parser.add_argument('--yamlpath', required=True, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 351 |  |  |                                help='The yaml-path of the element to match against.') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 352 |  |  |     create_parser.add_argument( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 353 |  |  |         '--match', required=True, help='A string value or regex providing the matching criteria. Required') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 354 |  |  |     create_parser.add_argument( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 355 |  |  |         '--namespace', help='The namespace of the Kubernetes object (optional for cluster-scoped objects)', default=None) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 356 |  |  |     create_parser.add_argument( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 357 |  |  |         '--title', help='A short description of the check.') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 358 |  |  |     create_parser.add_argument( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 359 |  |  |         '--url', help='The direct api path (metadata.selfLink) of the object, which overrides --type --name and --namespace options.') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 360 |  |  |     create_parser.add_argument( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 361 |  |  |         '--description', help='A human-readable description of the provided matching criteria.') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 362 |  |  |     create_parser.add_argument( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 363 |  |  |         '--regex', default=False, action="store_true", help='treat the --match value as a regex') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 364 |  |  |     create_parser.add_argument( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 365 |  |  |         '--match-entity', help='the entity_check value to apply, i.e., "all", "at least one", "none exist"') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 366 |  |  |     create_parser.add_argument( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 367 |  |  |         '--check-existence', help='check_existence` value for the `yamlfilecontent_test`.') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 368 |  |  |     create_parser.add_argument( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 369 |  |  |         '--negate', default=False, action="store_true", help='negate the given matching criteria (does NOT match). Default is false.') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 370 |  |  |     create_parser.add_argument( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 371 |  |  |         '--identifiers', default="TBD", help='an identifier for the rule (CCE number)') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 372 |  |  |     create_parser.add_argument( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 373 |  |  |         '--severity', default="unknown", help='the severity of the rule.') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 374 |  |  |     create_parser.set_defaults(func=createFunc) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 375 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 376 |  |  |     cluster_test_parser = subparser.add_parser( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 377 |  |  |         'cluster-test', help='Test a rule on a running OCP cluster using the compliance-operator.') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 378 |  |  |     cluster_test_parser.add_argument( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 379 |  |  |         '--rule', required=True, help='The name of the rule to test. Required.') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 380 |  |  |     cluster_test_parser.add_argument( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 381 |  |  |         '--skip-deploy', default=False, action="store_true", help='Skip deploying the compliance-operator. Default is to deploy.') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 382 |  |  |     cluster_test_parser.add_argument( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 383 |  |  |         '--skip-build', default=False, action="store_true", help='Skip building and pushing the datastream. Default is true.') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 384 |  |  |     cluster_test_parser.set_defaults(func=clusterTestFunc) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 385 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 386 |  |  |     test_parser = subparser.add_parser( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 387 |  |  |         'test', help='Test a rule locally against a directory of mocked object files using podman and an oscap container.') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 388 |  |  |     test_parser.add_argument('--rule', required=True, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 389 |  |  |                              help='The name of the rule to test.') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 390 |  |  |     test_parser.add_argument( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 391 |  |  |         '--contentdir', default="./build", help='The path to the directory containing the datastream') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 392 |  |  |     test_parser.add_argument( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 393 |  |  |         '--skip-build', default=False, action="store_true", help='Skip building the datastream. Default is false.') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 394 |  |  |     test_parser.add_argument('--objectdir', default="/tmp", | 
            
                                                                                                            
                            
            
                                    
            
            
                | 395 |  |  |                              help='The path to a directory structure of yaml objects to test against.') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 396 |  |  |     test_parser.set_defaults(func=testFunc) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 397 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 398 |  |  |     args = parser.parse_args() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 399 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 400 |  |  |     return args.func(args) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 401 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 402 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 403 |  |  | if __name__ == "__main__": | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 404 |  |  |     sys.exit(main()) | 
            
                                                        
            
                                    
            
            
                | 405 |  |  |  |