├── CIS-Audit-Reqs-Windows2019Server.xlsx ├── LICENSE ├── README.md ├── auditpolcis.py ├── cis-benchmarks.yaml ├── genyaml.py └── requirements.txt /CIS-Audit-Reqs-Windows2019Server.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SevenStones/auditpolCIS/44cfeb52491c5ceeba3c10e5aff21a91c6585254/CIS-Audit-Reqs-Windows2019Server.xlsx -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023, Seven Stones Information Security Limited. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 1. Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | 2. Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | 3. All advertising materials mentioning features or use of this software 12 | must display the following acknowledgement: 13 | This product includes software developed by the Seven Stones Information 14 | Security Limited. 15 | 4. Neither the name of the Seven Stones Information Security nor the 16 | names of its contributors may be used to endorse or promote products 17 | derived from this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ''AS IS'' AND ANY 20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 28 | USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # auditpolCIS 2 | CIS Benchmark testing of Windows SIEM configuration 3 | 4 | This is an application for testing the configuration of Windows Audit Policy settings against the CIS Benchmark recommended settings. A few points: 5 | 6 | - The tested system was Windows Server 2019, and the benchmark used was also Windows Server 2019. 7 | - The script connects with SSH. SSH is included with Windows Server 2019, it just has to be enabled. If you would like to see WinRM (or other) 8 | connection types, let me know or send a PR. 9 | - Some tests are included here which were not included in the CIS guide. The recommended settings for these Subcategories are based on the logging volume 10 | for these events, versus the security value. In nearly all cases, the recommendation is to turn off auditing for these settings. 11 | - The YAML file cis-benchmarks.yaml is the YAML representation of the CIS Benchmark guideline for each Subcategory. 12 | - The command run under SSH is auditpol /get /category:* 13 | 14 | The automated assessment results from AuditpolCIS, as it's based on CIS Benchmarks, helps in the support of meeting audit requirements for a number of programs, not least PCI-DSS: 15 | 16 | - Audit account logon events: Helps in monitoring and logging all attempts to authenticate user credentials (PCI-DSS Requirement 10.2.4). 17 | - Audit object access: Monitors access to objects like files, folders, and registry keys that store cardholder data (PCI-DSS Requirement 10.2.1). 18 | - Audit privilege use: Logs any event where a user exercises a user right or privilege (PCI-DSS Requirement 10.2.2) 19 | 20 | Note that the script also generates output relevant to other audit/compliance/regulatory requirements to do with the retention of events data. 21 | Local log files sizes and retention policies are useful in assessing compliance with e.g. PCI-DSS 4 5.3.4 and 10.5.1 requirements. 22 | 23 | ![image](https://user-images.githubusercontent.com/1404877/232906246-0feec791-7395-4196-9437-ce243b5a9361.png) 24 | 25 | Further details on usage and other background info is at https://www.seven-stones.biz/blog/auditpolcis-automating-windows-siem-cis-benchmarks-testing/ 26 | -------------------------------------------------------------------------------- /auditpolcis.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import paramiko 4 | import re 5 | import socket 6 | from dotenv import load_dotenv 7 | from tabulate import tabulate 8 | from colorama import Fore, Style, init 9 | 10 | def load_target_variables(): 11 | import os 12 | 13 | def load_env_file(env_file_path): 14 | if not os.path.exists(env_file_path): 15 | print(f"Error: Unable to find or open {env_file_path}") 16 | exit(1) 17 | 18 | load_dotenv(dotenv_path=env_file_path) 19 | 20 | def get_env_var(key): 21 | try: 22 | value = os.environ[key] 23 | except KeyError: 24 | print(f"Error: {key} not found in .env file") 25 | exit(1) 26 | return value 27 | 28 | # Load variables from .env file 29 | load_env_file(".env") 30 | 31 | # Read variables with error handling 32 | hostname = get_env_var("HOSTNAME") 33 | username = get_env_var("USERNAME") 34 | password = get_env_var("PASSWORD") 35 | 36 | return [hostname, username, password] 37 | 38 | def retention(ssh, secrets): 39 | 40 | hostname, username, password = secrets 41 | commands = [ 42 | 'C:\\Windows\\System32\\wevtutil.exe gl System', 43 | 'C:\\Windows\\System32\\wevtutil.exe gl Application', 44 | 'C:\\Windows\\System32\\wevtutil.exe gl Security' 45 | ] 46 | 47 | try: 48 | # Authenticate to Windows Server 49 | ssh.connect(hostname, username=username, password=password, timeout=10) 50 | 51 | except paramiko.AuthenticationException: 52 | print("[" + Fore.RED + "fail" + Style.RESET_ALL + "] Error: SSH Authentication failed. Please check your " 53 | "username and password.") 54 | except paramiko.SSHException: 55 | print("[" + Fore.RED + "fail" + Style.RESET_ALL + "] Error: An SSH-related error occurred.") 56 | except Exception as e: 57 | print("[" + Fore.RED + "fail" + Style.RESET_ALL + "] " + str(e)) 58 | else: 59 | for command in commands: 60 | print(f"Executing command: {command}") 61 | print("Here's the highlights...") 62 | print("\n") 63 | # Execute the command 64 | stdin, stdout, stderr = ssh.exec_command(command) 65 | 66 | # Get the output and error 67 | output = stdout.read().decode() 68 | error = stderr.read().decode().strip() 69 | 70 | if error: 71 | print(f"An error occurred: {error}") 72 | else: 73 | for line in output.split('\n'): 74 | if any(keyword in line for keyword in ["logFileName", "retention", "autoBackup", "maxSize"]): 75 | print(line.strip()) 76 | print("\n") 77 | 78 | def get_dict_from_yaml(): 79 | 80 | import yaml 81 | from pathlib import Path 82 | 83 | yaml_dict = {} 84 | 85 | def load_yaml_file(file_path): 86 | try: 87 | with open(file_path, 'r') as file: 88 | yaml_dict = yaml.safe_load(file) 89 | return yaml_dict 90 | except FileNotFoundError: 91 | print(f"Error: File not found - {file_path}") 92 | except yaml.YAMLError as e: 93 | print(f"Error: Invalid YAML format in {file_path} - {e}") 94 | 95 | file_path = 'cis-benchmarks.yaml' 96 | 97 | yaml_dict = load_yaml_file(file_path) 98 | 99 | # if yaml_dict is not None: 100 | # print("YAML file loaded successfully:") 101 | # print(yaml_dict) 102 | 103 | return yaml_dict 104 | 105 | 106 | def filter_trash(cis_dict, auditpol_dict): 107 | 108 | missing_in_auditpol = {} 109 | missing_in_cis = {} 110 | 111 | for category, subcategories in cis_dict.items(): 112 | if category not in auditpol_dict: 113 | missing_in_auditpol[category] = set(subcategories.keys()) 114 | else: 115 | for subcategory in subcategories: 116 | if subcategory not in auditpol_dict[category]: 117 | if category not in missing_in_auditpol: 118 | missing_in_auditpol[category] = set() 119 | missing_in_auditpol[category].add(subcategory) 120 | 121 | for category, subcategories in auditpol_dict.items(): 122 | if category not in cis_dict: 123 | missing_in_cis[category] = set(subcategories.keys()) 124 | else: 125 | for subcategory in subcategories: 126 | if subcategory not in cis_dict[category]: 127 | if category not in missing_in_cis: 128 | missing_in_cis[category] = set() 129 | missing_in_cis[category].add(subcategory) 130 | 131 | for category, subcategories in list(cis_dict.items()): 132 | if category not in auditpol_dict: 133 | del cis_dict[category] 134 | else: 135 | for subcategory in list(subcategories.keys()): 136 | if subcategory not in auditpol_dict[category]: 137 | del cis_dict[category][subcategory] 138 | 139 | for category, subcategories in list(auditpol_dict.items()): 140 | if category not in cis_dict: 141 | del auditpol_dict[category] 142 | else: 143 | for subcategory in list(subcategories.keys()): 144 | if subcategory not in cis_dict[category]: 145 | del auditpol_dict[category][subcategory] 146 | 147 | return [auditpol_dict, cis_dict, missing_in_auditpol, missing_in_cis] 148 | 149 | 150 | def test_audit_policy(ssh, secrets): 151 | 152 | hostname, username, password = secrets 153 | 154 | try: 155 | # Authenticate to Windows Server 156 | ssh.connect(hostname, username=username, password=password, timeout=10) 157 | 158 | except paramiko.AuthenticationException: 159 | print("[" + Fore.RED + "fail" + Style.RESET_ALL + "] Error: SSH Authentication failed. Please check your " 160 | "username and password.") 161 | except paramiko.SSHException: 162 | print("[" + Fore.RED + "fail" + Style.RESET_ALL + "] Error: An SSH-related error occurred.") 163 | except Exception as e: 164 | print("[" + Fore.RED + "fail" + Style.RESET_ALL + "] " + str(e)) 165 | else: 166 | # Run the 'auditpol /get /category:*' command over SSH on the remote server 167 | stdin, stdout, stderr = ssh.exec_command('auditpol /get /category:*') 168 | 169 | error = stderr.read() 170 | if error: 171 | print(f"Error encountered: {error.decode('utf-8')}") 172 | exit(1) 173 | 174 | output = stdout.read().decode('utf-8') 175 | 176 | # Split the string into lines 177 | lines = output.split('\n') 178 | 179 | # Remove the first two lines and join the remaining lines 180 | output = '\n'.join(lines[2:]) 181 | 182 | text = output 183 | category_pattern = r'^(\w+.*?)(\r)?$' 184 | subcategory_pattern = r'^( {2})([^ ]+.*?)(?=\s{3,})(.*\S)' 185 | 186 | auditpol_dict = {} 187 | current_category = None 188 | 189 | for line in text.split('\n'): 190 | category_match = re.match(category_pattern, line) 191 | subcategory_match = re.match(subcategory_pattern, line) 192 | 193 | if category_match: 194 | current_category = category_match.group(1) 195 | auditpol_dict[current_category] = {} 196 | elif subcategory_match: 197 | subcategory = subcategory_match.group(2).strip() 198 | setting = subcategory_match.group(3).strip() 199 | auditpol_dict[current_category][subcategory] = setting 200 | 201 | cis_dict = get_dict_from_yaml() 202 | 203 | filter_result = filter_trash(cis_dict, auditpol_dict) 204 | auditpol_dict = filter_result[0] 205 | cis_dict = filter_result[1] 206 | in_cisyaml_but_missing_from_auditpol = filter_result[2] 207 | in_auditpol_but_missing_from_cisyaml = filter_result[3] 208 | 209 | results = {} 210 | 211 | for main_key in auditpol_dict: 212 | results[main_key] = {} 213 | for sub_key in auditpol_dict[main_key]: 214 | if auditpol_dict[main_key][sub_key] == cis_dict[main_key][sub_key]['CIS Recommended']: 215 | results[main_key][sub_key] = {'CIS_included': cis_dict[main_key][sub_key]['CIS Benchmark'], 216 | 'result_expected': cis_dict[main_key][sub_key]['CIS Recommended'], 217 | 'result': auditpol_dict[main_key][sub_key], 218 | 'verdict': 'pass'} 219 | else: 220 | results[main_key][sub_key] = {'CIS_included': cis_dict[main_key][sub_key]['CIS Benchmark'], 221 | 'result_expected': cis_dict[main_key][sub_key]['CIS Recommended'], 222 | 'result': auditpol_dict[main_key][sub_key], 223 | 'verdict': 'fail'} 224 | 225 | init(autoreset=True) # Automatically reset colorama styles after each print 226 | 227 | headers = ["Category", "Subcategory", "CIS Benchmark?", "Test Result (result [expected])", "Verdict"] 228 | 229 | table_data = [] 230 | cis_benchmark_count = 0 231 | cis_benchmark_pass_count = 0 232 | 233 | for category, subcategories in results.items(): 234 | for subcategory, details in subcategories.items(): 235 | CIS_included = details['CIS_included'] 236 | result = details['result'] 237 | result_expected = details['result_expected'] 238 | verdict = details['verdict'] 239 | 240 | if verdict == 'fail': 241 | colored_verdict = Fore.RED + verdict + Style.RESET_ALL 242 | elif verdict == 'pass': 243 | colored_verdict = Fore.GREEN + verdict + Style.RESET_ALL 244 | else: 245 | colored_verdict = verdict 246 | 247 | if CIS_included: 248 | cis_benchmark_count += 1 249 | if verdict == 'pass': 250 | cis_benchmark_pass_count += 1 251 | 252 | table_data.append( 253 | [category, subcategory, CIS_included, f"{result} [{result_expected}]", colored_verdict]) 254 | 255 | print(tabulate(table_data, headers=headers, tablefmt='grid')) 256 | 257 | # Print the final score 258 | print("\n") 259 | print(f"CIS Benchmarks Test Score: {cis_benchmark_pass_count} / {cis_benchmark_count}") 260 | 261 | if in_cisyaml_but_missing_from_auditpol: 262 | print("\n") 263 | print("Note: some [Sub]categories were found to be in the CIS Benchmarks YAML file, but could not" 264 | " be matched with output [Sub]categories from the auditpol command:") 265 | for category, subcategory in in_cisyaml_but_missing_from_auditpol.items(): 266 | for j in subcategory: 267 | print(f"\t{category} --> {j}") 268 | 269 | if in_auditpol_but_missing_from_cisyaml: 270 | print("\n") 271 | print("Note: some [Sub]categories were found to be in the auditpol command output, but could not be " 272 | "matched with output [Sub]categories from the CIS Benchmarks YAML file:") 273 | for category, subcategory in in_auditpol_but_missing_from_cisyaml.items(): 274 | for j in subcategory: 275 | print(f"\t{category} --> {j}") 276 | 277 | finally: 278 | # Close the SSH connection 279 | ssh.close() 280 | 281 | 282 | if __name__ == '__main__': 283 | # Set up SSH client 284 | ssh = paramiko.SSHClient() 285 | ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 286 | 287 | def check_ssh_availability(host, port=22, timeout=10): 288 | try: 289 | sock = socket.create_connection((host, port), timeout) 290 | sock.close() 291 | return True 292 | except socket.error: 293 | return False 294 | 295 | secrets = load_target_variables() 296 | test_audit_policy(ssh, secrets) 297 | print("\n") 298 | print("Log Size and Retention Settings") 299 | print("-------------------------------") 300 | print("\n") 301 | 302 | retention(ssh, secrets) 303 | -------------------------------------------------------------------------------- /cis-benchmarks.yaml: -------------------------------------------------------------------------------- 1 | "Account Logon": 2 | "Credential Validation": 3 | "CIS Benchmark": true 4 | "CIS Recommended": "Success and Failure" 5 | "Kerberos Authentication Service": 6 | "CIS Benchmark": true 7 | "CIS Recommended": "Success and Failure" 8 | "Kerberos Service Ticket Operations": 9 | "CIS Benchmark": true 10 | "CIS Recommended": "Success and Failure" 11 | "Other Account Logon Events": 12 | "CIS Benchmark": false 13 | "CIS Recommended": "No Auditing" 14 | "Account Management": 15 | "Application Group Management": 16 | "CIS Benchmark": true 17 | "CIS Recommended": "Success and Failure" 18 | "Computer Account Management": 19 | "CIS Benchmark": true 20 | "CIS Recommended": "Success" 21 | "Distribution Group Management": 22 | "CIS Benchmark": true 23 | "CIS Recommended": "Success" 24 | "Other Account Management Events": 25 | "CIS Benchmark": true 26 | "CIS Recommended": "Success" 27 | "Security Group Management": 28 | "CIS Benchmark": true 29 | "CIS Recommended": "Success" 30 | "User Account Management": 31 | "CIS Benchmark": true 32 | "CIS Recommended": "Success and Failure" 33 | "DS Access": 34 | "Detailed Directory Service Replication": 35 | "CIS Benchmark": false 36 | "CIS Recommended": "Success" 37 | "Directory Service Access": 38 | "CIS Benchmark": false 39 | "CIS Recommended": "No Auditing" 40 | "Directory Service Changes": 41 | "CIS Benchmark": false 42 | "CIS Recommended": "Success" 43 | "Directory Service Replication": 44 | "CIS Benchmark": false 45 | "CIS Recommended": "Success and Failure" 46 | "Detailed Tracking": 47 | "DPAPI Activity": 48 | "CIS Benchmark": false 49 | "CIS Recommended": "No auditing" 50 | "Plug and Play Events": 51 | "CIS Benchmark": true 52 | "CIS Recommended": "Success" 53 | "Process Creation": 54 | "CIS Benchmark": true 55 | "CIS Recommended": "Success" 56 | "Process Termination": 57 | "CIS Benchmark": false 58 | "CIS Recommended": "Success" 59 | "RPC Events": 60 | "CIS Benchmark": false 61 | "CIS Recommended": "Success" 62 | "Token Right Adjusted Events": 63 | "CIS Benchmark": false 64 | "CIS Recommended": "Success" 65 | "Logon/Logoff": 66 | "Account Lockout": 67 | "CIS Benchmark": true 68 | "CIS Recommended": "Failure" 69 | "Group Membership": 70 | "CIS Benchmark": true 71 | "CIS Recommended": "Success" 72 | "IPsec Extended Mode": 73 | "CIS Benchmark": false 74 | "CIS Recommended": "No Auditing" 75 | "IPsec Main Mode": 76 | "CIS Benchmark": false 77 | "CIS Recommended": "No Auditing" 78 | "IPsec Quick Mode": 79 | "CIS Benchmark": false 80 | "CIS Recommended": "No Auditing" 81 | "Logoff": 82 | "CIS Benchmark": true 83 | "CIS Recommended": "Success" 84 | "Logon": 85 | "CIS Benchmark": true 86 | "CIS Recommended": "Success and Failure" 87 | "Network Policy Server": 88 | "CIS Benchmark": false 89 | "CIS Recommended": "Success" 90 | "Other Logon/Logoff Events": 91 | "CIS Benchmark": true 92 | "CIS Recommended": "Success and Failure" 93 | "Special Logon": 94 | "CIS Benchmark": true 95 | "CIS Recommended": "Success" 96 | "User / Device Claims": 97 | "CIS Benchmark": false 98 | "CIS Recommended": "No Auditing" 99 | "Object Access": 100 | "Application Generated": 101 | "CIS Benchmark": false 102 | "CIS Recommended": "No Auditing" 103 | "Central Policy Staging": 104 | "CIS Benchmark": false 105 | "CIS Recommended": "Success" 106 | "Certification Services": 107 | "CIS Benchmark": false 108 | "CIS Recommended": "Success" 109 | "Detailed File Share": 110 | "CIS Benchmark": true 111 | "CIS Recommended": "Failure" 112 | "File Share": 113 | "CIS Benchmark": true 114 | "CIS Recommended": "Success and Failure" 115 | "File System": 116 | "CIS Benchmark": false 117 | "CIS Recommended": "No Auditing" 118 | "Filtering Platform Connection": 119 | "CIS Benchmark": false 120 | "CIS Recommended": "No Auditing" 121 | "Filtering Platform Packet Drop": 122 | "CIS Benchmark": false 123 | "CIS Recommended": "No Auditing" 124 | "Handle Manipulation": 125 | "CIS Benchmark": false 126 | "CIS Recommended": "No Auditing" 127 | "Kernel Object": 128 | "CIS Benchmark": false 129 | "CIS Recommended": "No Auditing" 130 | "Other Object Access Events": 131 | "CIS Benchmark": true 132 | "CIS Recommended": "Success and Failure" 133 | "Registry": 134 | "CIS Benchmark": false 135 | "CIS Recommended": "No Auditing" 136 | "Removable Storage": 137 | "CIS Benchmark": true 138 | "CIS Recommended": "Success and Failure" 139 | "SAM": 140 | "CIS Benchmark": false 141 | "CIS Recommended": "No Auditing" 142 | "Policy Change": 143 | "Audit Policy Change": 144 | "CIS Benchmark": true 145 | "CIS Recommended": "Success" 146 | "Authentication Policy Change": 147 | "CIS Benchmark": true 148 | "CIS Recommended": "Success" 149 | "Authorization Policy Change": 150 | "CIS Benchmark": true 151 | "CIS Recommended": "Success" 152 | "Filtering Platform Policy Change": 153 | "CIS Benchmark": false 154 | "CIS Recommended": "No Auditing" 155 | "MPSSVC Rule-Level Policy Change": 156 | "CIS Benchmark": true 157 | "CIS Recommended": "Success and Failure" 158 | "Other Policy Change Events": 159 | "CIS Benchmark": true 160 | "CIS Recommended": "Failure" 161 | "Privilege Use": 162 | "Non Sensitive Privilege Use": 163 | "CIS Benchmark": false 164 | "CIS Recommended": "No Auditing" 165 | "Other Privilege Use Events": 166 | "CIS Benchmark": false 167 | "CIS Recommended": "No Auditing" 168 | "Sensitive Privilege Use": 169 | "CIS Benchmark": true 170 | "CIS Recommended": "Success and Failure" 171 | "System": 172 | "IPsec Driver": 173 | "CIS Benchmark": true 174 | "CIS Recommended": "Success and Failure" 175 | "Other System Events": 176 | "CIS Benchmark": true 177 | "CIS Recommended": "Success and Failure" 178 | "Security State Change": 179 | "CIS Benchmark": true 180 | "CIS Recommended": "Success" 181 | "Security System Extension": 182 | "CIS Benchmark": true 183 | "CIS Recommended": "Success" 184 | "System Integrity": 185 | "CIS Benchmark": true 186 | "CIS Recommended": "Success and Failure" 187 | -------------------------------------------------------------------------------- /genyaml.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | genyaml.py can be used to create the cis-benchmarks.yaml file from the spreadsheet 5 | ('CIS-Audit-Reqs-Windows2019Server.xlsx'). So edit the spreadsheet, or the YAML 6 | file directly to customise e.g. Subcategory names to match auditpol command output, 7 | or change the verdicts. 8 | """ 9 | 10 | import pandas as pd 11 | from collections import defaultdict 12 | from ruamel.yaml import YAML 13 | from ruamel.yaml.scalarstring import DoubleQuotedScalarString as dq 14 | 15 | # Read the Excel file 16 | excel_file = 'CIS-Audit-Reqs-Windows2019Server.xlsx' 17 | df = pd.read_excel(excel_file, engine='openpyxl') 18 | 19 | # Remove leading and trailing whitespace from column names 20 | df.columns = [col.strip() for col in df.columns] 21 | 22 | # Construct the YAML data 23 | yaml_data = defaultdict(dict) 24 | 25 | for _, row in df.iterrows(): 26 | category = dq(row['Category'].strip()) 27 | subcategory = dq(row['Subcategory'].strip()) 28 | 29 | # Convert "yes"/"no" to boolean values for "CIS Benchmark" 30 | cis_benchmark = True if row['CIS Benchmark'].strip().lower() == 'yes' else False 31 | 32 | # Remove trailing spaces and single quotes from "CIS Recommended" values, and add quotation marks 33 | cis_recommended = dq(row['CIS Recommended'].strip().strip("'")) 34 | 35 | yaml_data[category][subcategory] = { 36 | dq('CIS Benchmark'): cis_benchmark, 37 | dq('CIS Recommended'): cis_recommended 38 | } 39 | 40 | # Write the YAML data to a file 41 | yaml = YAML() 42 | yaml.indent(mapping=2, sequence=4, offset=2) 43 | with open('cis-benchmarks.yaml', 'w') as yaml_file: 44 | yaml.dump(dict(yaml_data), yaml_file) 45 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | auditpol==1.1.0 2 | bcrypt==4.0.1 3 | cffi==1.15.1 4 | colorama==0.4.6 5 | cryptography==40.0.1 6 | et-xmlfile==1.1.0 7 | numpy==1.24.2 8 | openpyxl==3.1.2 9 | pandas==2.0.0 10 | paramiko==3.1.0 11 | pycparser==2.21 12 | PyNaCl==1.5.0 13 | python-dateutil==2.8.2 14 | python-dotenv==1.0.0 15 | pytz==2023.3 16 | PyYAML==6.0 17 | ruamel.yaml==0.17.21 18 | ruamel.yaml.clib==0.2.7 19 | six==1.16.0 20 | tabulate==0.9.0 21 | tzdata==2023.3 22 | --------------------------------------------------------------------------------