parv.ashwani преди 6 месеца
родител
ревизия
b54545dbcc
променени са 2 файла, в които са добавени 649 реда и са изтрити 0 реда
  1. 25 0
      example_cipher.txt
  2. 624 0
      scanner.py

+ 25 - 0
example_cipher.txt

@@ -0,0 +1,25 @@
+Cipher Suites (24 suites)
+    Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA (0x0035)
+    Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f)
+    Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA256 (0x003d)
+    Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA256 (0x003c)
+    Cipher Suite: TLS_RSA_WITH_AES_256_GCM_SHA384 (0x009d)
+    Cipher Suite: TLS_RSA_WITH_AES_128_GCM_SHA256 (0x009c)
+    Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014)
+    Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)
+    Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (0xc028)
+    Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (0xc027)
+    Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
+    Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
+    Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (0xc00a)
+    Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (0xc009)
+    Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 (0xc024)
+    Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 (0xc023)
+    Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c)
+    Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b)
+    Cipher Suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 (0x006b)
+    Cipher Suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 (0x0067)
+    Cipher Suite: TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 (0x009f)
+    Cipher Suite: TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 (0x009e)
+    Cipher Suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA (0x0039)
+    Cipher Suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA (0x0033)

+ 624 - 0
scanner.py

@@ -0,0 +1,624 @@
+#!/usr/bin/env python3
+"""
+CipherScanner - NS Configuration Cipher Suite Compliance Checker
+Reads cipher suites from scan output and checks against NS configuration file
+"""
+
+import re
+import json
+import argparse
+import sys
+from datetime import datetime
+from collections import defaultdict
+
+class CipherScanner:
+    """Main CipherScanner class for analyzing cipher suite compliance"""
+    
+    def __init__(self, ns_conf_file, cipher_file):
+        """Initialize CipherScanner with configuration files"""
+        self.ns_conf_file = ns_conf_file
+        self.cipher_file = cipher_file
+        self.iana_ciphers = []
+        self.ns_config = {}
+        self.analysis_results = {}
+        
+    def run_scan(self):
+        """Run the complete cipher scan and analysis"""
+        print(f"""
+╔{'═'*78}╗
+║{'CIPHERSCANNER':^78}║
+╠{'═'*78}╣
+║ NS Configuration: {self.ns_conf_file:<56}║
+║ Cipher File: {self.cipher_file:<58}║
+╚{'═'*78}╝
+        """)
+        
+        # Parse cipher suites
+        self.iana_ciphers, _ = self.parse_ciphers_from_text(self.cipher_file)
+        if not self.iana_ciphers:
+            print("❌ No cipher suites parsed. Scan aborted.")
+            return False
+            
+        # Parse NS configuration
+        self.ns_config = self.parse_ns_config(self.ns_conf_file)
+        if not self.ns_config:
+            print("❌ No NS configuration parsed. Scan aborted.")
+            return False
+            
+        # Analyze compliance
+        self.analysis_results = self.analyze_cipher_compliance()
+        
+        # Generate report
+        self.generate_report()
+        
+        return True
+        
+    def parse_ciphers_from_text(self, text_file):
+        """
+        Parse cipher suites from text file with format:
+        Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA (0x0035)
+        """
+        ciphers = []
+        hex_to_name = {}
+        
+        try:
+            with open(text_file, 'r') as f:
+                lines = f.readlines()
+                
+            cipher_pattern = re.compile(
+                r'Cipher Suite:\s*([\w_]+)\s+\(([^)]+)\)',
+                re.IGNORECASE
+            )
+            
+            for line in lines:
+                match = cipher_pattern.search(line.strip())
+                if match:
+                    cipher_name = match.group(1).strip()
+                    hex_value = match.group(2).strip()
+                    ciphers.append(cipher_name)
+                    hex_to_name[hex_value] = cipher_name
+                    
+            print(f"✅ CipherScanner parsed {len(ciphers)} cipher suites from {text_file}")
+            return ciphers, hex_to_name
+            
+        except FileNotFoundError:
+            print(f"❌ CipherScanner: File not found: {text_file}")
+            return [], {}
+        except Exception as e:
+            print(f"❌ CipherScanner error parsing cipher file: {e}")
+            return [], {}
+    
+    def parse_ns_config(self, ns_conf_file):
+        """
+        Parse NS configuration file for SSL/TLS related configurations
+        """
+        ssl_configs = {
+            'cipher_groups': [],
+            'ssl_profiles': [],
+            'ssl_vservers': [],
+            'ssl_parameters': {}
+        }
+        
+        try:
+            with open(ns_conf_file, 'r') as f:
+                content = f.read()
+            
+            # Remove comments and blank lines for cleaner parsing
+            lines = []
+            for line in content.split('\n'):
+                line = line.strip()
+                # Remove comments (starting with # or //)
+                line = re.sub(r'(#|//).*$', '', line).strip()
+                if line:
+                    lines.append(line)
+            
+            # Join lines for multi-line command parsing
+            content_clean = '\n'.join(lines)
+            
+            print(f"\n🔍 CipherScanner parsing NS configuration from {ns_conf_file}")
+            
+            # Find cipher groups - improved regex pattern
+            cipher_group_pattern = re.compile(
+                r'add\s+ssl\s+cipher\s+([^\s]+)\s+(.*?)(?=\n\s*(?:add|set|bind|\Z))',
+                re.IGNORECASE | re.DOTALL | re.MULTILINE
+            )
+            
+            matches = cipher_group_pattern.findall(content_clean)
+            for match in matches:
+                group_name = match[0]
+                config = match[1].strip()
+                
+                # Extract ciphers from group configuration
+                cipher_list = []
+                
+                # Try different patterns to find cipher names
+                cipher_matches = re.findall(r'["\']?([A-Z0-9_\-]+)["\']?', config)
+                for cipher_candidate in cipher_matches:
+                    # Filter out common non-cipher patterns
+                    if (len(cipher_candidate) > 5 and 
+                        '-' in cipher_candidate and 
+                        not cipher_candidate.startswith(('PRIORITY', 'DEFAULT'))):
+                        cipher_list.append(cipher_candidate)
+                
+                # Alternative: Look for cipherName parameter
+                cipher_name_match = re.search(r'cipherName\s*[:=]\s*["\']([^"\']+)["\']', config, re.IGNORECASE)
+                if cipher_name_match:
+                    cipher_string = cipher_name_match.group(1)
+                    cipher_list = [c.strip() for c in cipher_string.split('-') if c.strip()]
+                
+                ssl_configs['cipher_groups'].append({
+                    'name': group_name,
+                    'ciphers': cipher_list,
+                    'raw_config': config[:500] + "..." if len(config) > 500 else config
+                })
+            
+            # Find SSL vservers
+            vserver_pattern = re.compile(
+                r'add\s+lb\s+vserver\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+).*?(?=\n\s*(?:add|set|bind|\Z))',
+                re.IGNORECASE | re.DOTALL | re.MULTILINE
+            )
+            
+            vserver_matches = vserver_pattern.findall(content_clean)
+            for match in vserver_matches:
+                vserver_name = match[0]
+                protocol = match[1]
+                ip = match[2]
+                port = match[3]
+                
+                # Look for SSL bindings for this vserver
+                binding_pattern = re.compile(
+                    r'bind\s+ssl\s+vserver\s+' + re.escape(vserver_name) + r'\s+(.*?)(?=\n\s*(?:bind|add|set|\Z))',
+                    re.IGNORECASE | re.DOTALL | re.MULTILINE
+                )
+                
+                binding_match = binding_pattern.search(content_clean)
+                ssl_config = {
+                    'name': vserver_name,
+                    'protocol': protocol,
+                    'ip': ip,
+                    'port': port,
+                    'ssl_profile': None,
+                    'certificate': None,
+                    'cipher_group': None
+                }
+                
+                if binding_match:
+                    binding_config = binding_match.group(1)
+                    
+                    # Extract cipher group
+                    cipher_group_match = re.search(r'-cipherName\s+(\S+)', binding_config, re.IGNORECASE)
+                    if cipher_group_match:
+                        ssl_config['cipher_group'] = cipher_group_match.group(1)
+                    
+                    # Extract SSL profile
+                    profile_match = re.search(r'-sslProfile\s+(\S+)', binding_config, re.IGNORECASE)
+                    if profile_match:
+                        ssl_config['ssl_profile'] = profile_match.group(1)
+                    
+                    # Extract certificate
+                    cert_match = re.search(r'-certkeyName\s+(\S+)', binding_config, re.IGNORECASE)
+                    if cert_match:
+                        ssl_config['certificate'] = cert_match.group(1)
+                
+                # Only include SSL/TLS vservers
+                if (protocol.upper() in ['SSL', 'SSL_TCP', 'SSL_BRIDGE', 'TCP'] and 
+                    port in ['443', '8443', '9443', '10443']):
+                    ssl_configs['ssl_vservers'].append(ssl_config)
+            
+            # Find SSL profiles
+            profile_pattern = re.compile(
+                r'add\s+ssl\s+profile\s+(\S+)\s+(.*?)(?=\n\s*(?:add|set|bind|\Z))',
+                re.IGNORECASE | re.DOTALL | re.MULTILINE
+            )
+            
+            profile_matches = profile_pattern.findall(content_clean)
+            for match in profile_matches:
+                profile_name = match[0]
+                config = match[1]
+                
+                # Extract cipher settings from profile
+                cipher_match = re.search(r'-cipherName\s+(\S+)', config, re.IGNORECASE)
+                cipher_group = cipher_match.group(1) if cipher_match else None
+                
+                # Extract other SSL settings
+                tls_settings = {
+                    'tls11_enabled': 'NOT_FOUND',
+                    'tls12_enabled': 'NOT_FOUND',
+                    'tls13_enabled': 'NOT_FOUND'
+                }
+                
+                for tls_ver in ['tls11', 'tls12', 'tls13']:
+                    tls_match = re.search(rf'-{tls_ver}\s+(\S+)', config, re.IGNORECASE)
+                    if tls_match:
+                        tls_settings[f'{tls_ver}_enabled'] = tls_match.group(1)
+                
+                ssl_configs['ssl_profiles'].append({
+                    'name': profile_name,
+                    'cipher_group': cipher_group,
+                    **tls_settings,
+                    'raw_config': config[:300] + "..." if len(config) > 300 else config
+                })
+            
+            # Find SSL parameter settings
+            param_patterns = [
+                (r'set\s+ssl\s+parameter\s+-ssl3\s+(\S+)', 'ssl3_enabled'),
+                (r'set\s+ssl\s+parameter\s+-tls1\s+(\S+)', 'tls1_enabled'),
+                (r'set\s+ssl\s+parameter\s+-tls11\s+(\S+)', 'tls11_enabled'),
+                (r'set\s+ssl\s+parameter\s+-tls12\s+(\S+)', 'tls12_enabled'),
+                (r'set\s+ssl\s+parameter\s+-tls13\s+(\S+)', 'tls13_enabled'),
+                (r'set\s+ssl\s+parameter\s+-denySSLReneg\s+(\S+)', 'deny_ssl_reneg'),
+            ]
+            
+            for pattern, key in param_patterns:
+                match = re.search(pattern, content_clean, re.IGNORECASE)
+                if match:
+                    ssl_configs['ssl_parameters'][key] = match.group(1)
+            
+            print(f"✅ CipherScanner found {len(ssl_configs['cipher_groups'])} cipher groups")
+            print(f"✅ CipherScanner found {len(ssl_configs['ssl_vservers'])} SSL vservers")
+            print(f"✅ CipherScanner found {len(ssl_configs['ssl_profiles'])} SSL profiles")
+            
+            return ssl_configs
+            
+        except FileNotFoundError:
+            print(f"❌ CipherScanner: File not found: {ns_conf_file}")
+            return {}
+        except Exception as e:
+            print(f"❌ CipherScanner error parsing NS configuration: {e}")
+            import traceback
+            traceback.print_exc()
+            return {}
+    
+    def map_ns_cipher_to_iana(self, ns_cipher):
+        """
+        Map NetScaler cipher names to IANA/RFC names
+        """
+        # Clean the cipher name
+        ns_cipher = ns_cipher.strip().upper()
+        
+        cipher_mappings = {
+            # AES CBC ciphers
+            'SSL3-RSA-AES-256-CBC-SHA': 'TLS_RSA_WITH_AES_256_CBC_SHA',
+            'SSL3-RSA-AES-128-CBC-SHA': 'TLS_RSA_WITH_AES_128_CBC_SHA',
+            'TLS1-RSA-AES-256-CBC-SHA': 'TLS_RSA_WITH_AES_256_CBC_SHA',
+            'TLS1-RSA-AES-128-CBC-SHA': 'TLS_RSA_WITH_AES_128_CBC_SHA',
+            'TLS1.2-RSA-AES-256-CBC-SHA': 'TLS_RSA_WITH_AES_256_CBC_SHA',
+            'TLS1.2-RSA-AES-128-CBC-SHA': 'TLS_RSA_WITH_AES_128_CBC_SHA',
+            'TLS1.2-RSA-AES-256-CBC-SHA256': 'TLS_RSA_WITH_AES_256_CBC_SHA256',
+            'TLS1.2-RSA-AES-128-CBC-SHA256': 'TLS_RSA_WITH_AES_128_CBC_SHA256',
+            
+            # AES GCM ciphers
+            'TLS1.2-RSA-AES-256-GCM-SHA384': 'TLS_RSA_WITH_AES_256_GCM_SHA384',
+            'TLS1.2-RSA-AES-128-GCM-SHA256': 'TLS_RSA_WITH_AES_128_GCM_SHA256',
+            
+            # ECDHE RSA ciphers
+            'TLS1.2-ECDHE-RSA-AES-256-CBC-SHA': 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA',
+            'TLS1.2-ECDHE-RSA-AES-128-CBC-SHA': 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA',
+            'TLS1.2-ECDHE-RSA-AES-256-CBC-SHA384': 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384',
+            'TLS1.2-ECDHE-RSA-AES-128-CBC-SHA256': 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256',
+            'TLS1.2-ECDHE-RSA-AES-256-GCM-SHA384': 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384',
+            'TLS1.2-ECDHE-RSA-AES-128-GCM-SHA256': 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256',
+            
+            # ECDHE ECDSA ciphers
+            'TLS1.2-ECDHE-ECDSA-AES-256-CBC-SHA': 'TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA',
+            'TLS1.2-ECDHE-ECDSA-AES-128-CBC-SHA': 'TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA',
+            'TLS1.2-ECDHE-ECDSA-AES-256-CBC-SHA384': 'TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384',
+            'TLS1.2-ECDHE-ECDSA-AES-128-CBC-SHA256': 'TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256',
+            'TLS1.2-ECDHE-ECDSA-AES-256-GCM-SHA384': 'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384',
+            'TLS1.2-ECDHE-ECDSA-AES-128-GCM-SHA256': 'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256',
+            
+            # DHE RSA ciphers
+            'TLS1.2-DHE-RSA-AES-256-CBC-SHA': 'TLS_DHE_RSA_WITH_AES_256_CBC_SHA',
+            'TLS1.2-DHE-RSA-AES-128-CBC-SHA': 'TLS_DHE_RSA_WITH_AES_128_CBC_SHA',
+            'TLS1.2-DHE-RSA-AES-256-CBC-SHA256': 'TLS_DHE_RSA_WITH_AES_256_CBC_SHA256',
+            'TLS1.2-DHE-RSA-AES-128-CBC-SHA256': 'TLS_DHE_RSA_WITH_AES_128_CBC_SHA256',
+            'TLS1.2-DHE-RSA-AES-256-GCM-SHA384': 'TLS_DHE_RSA_WITH_AES_256_GCM_SHA384',
+            'TLS1.2-DHE-RSA-AES-128-GCM-SHA256': 'TLS_DHE_RSA_WITH_AES_128_GCM_SHA256',
+            
+            # TLS 1.3 ciphers (NetScaler format)
+            'TLS1.3-AES256-GCM-SHA384': 'TLS_AES_256_GCM_SHA384',
+            'TLS1.3-AES128-GCM-SHA256': 'TLS_AES_128_GCM_SHA256',
+            'TLS1.3-CHACHA20-POLY1305-SHA256': 'TLS_CHACHA20_POLY1305_SHA256',
+            
+            # Weak/Deprecated ciphers
+            'SSL3-RSA-RC4-MD5': 'TLS_RSA_WITH_RC4_128_MD5',
+            'SSL3-RSA-RC4-SHA': 'TLS_RSA_WITH_RC4_128_SHA',
+            'SSL3-RSA-DES-CBC3-SHA': 'TLS_RSA_WITH_3DES_EDE_CBC_SHA',
+            'TLS1-RSA-RC4-MD5': 'TLS_RSA_WITH_RC4_128_MD5',
+            'TLS1-RSA-RC4-SHA': 'TLS_RSA_WITH_RC4_128_SHA',
+            
+            # Common NetScaler cipher patterns (simplified)
+            'TLS1.2-ECDHE-ECDSA-AES256-SHA384': 'TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384',
+            'TLS1.2-ECDHE-ECDSA-AES128-SHA256': 'TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256',
+            'TLS1.2-ECDHE-RSA-AES256-SHA384': 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384',
+            'TLS1.2-ECDHE-RSA-AES128-SHA256': 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256',
+        }
+        
+        # Direct match
+        if ns_cipher in cipher_mappings:
+            return cipher_mappings[ns_cipher]
+        
+        # Try to match patterns
+        # Remove version prefixes for pattern matching
+        cipher_without_version = ns_cipher
+        version_prefixes = ['SSL3-', 'TLS1-', 'TLS1.1-', 'TLS1.2-', 'TLS1.3-']
+        for prefix in version_prefixes:
+            if ns_cipher.startswith(prefix):
+                cipher_without_version = ns_cipher[len(prefix):]
+                break
+        
+        # Pattern-based mapping
+        if 'AES256-GCM-SHA384' in cipher_without_version and 'ECDHE' not in cipher_without_version and 'DHE' not in cipher_without_version:
+            return 'TLS_RSA_WITH_AES_256_GCM_SHA384'
+        elif 'AES128-GCM-SHA256' in cipher_without_version and 'ECDHE' not in cipher_without_version and 'DHE' not in cipher_without_version:
+            return 'TLS_RSA_WITH_AES_128_GCM_SHA256'
+        elif 'AES256-CBC-SHA256' in cipher_without_version and 'ECDHE' not in cipher_without_version and 'DHE' not in cipher_without_version:
+            return 'TLS_RSA_WITH_AES_256_CBC_SHA256'
+        elif 'AES128-CBC-SHA256' in cipher_without_version and 'ECDHE' not in cipher_without_version and 'DHE' not in cipher_without_version:
+            return 'TLS_RSA_WITH_AES_128_CBC_SHA256'
+        elif 'AES256-CBC-SHA' in cipher_without_version and 'ECDHE' not in cipher_without_version and 'DHE' not in cipher_without_version:
+            return 'TLS_RSA_WITH_AES_256_CBC_SHA'
+        elif 'AES128-CBC-SHA' in cipher_without_version and 'ECDHE' not in cipher_without_version and 'DHE' not in cipher_without_version:
+            return 'TLS_RSA_WITH_AES_128_CBC_SHA'
+        elif 'AES256-GCM-SHA384' in cipher_without_version and 'ECDHE-RSA' in cipher_without_version:
+            return 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384'
+        elif 'AES128-GCM-SHA256' in cipher_without_version and 'ECDHE-RSA' in cipher_without_version:
+            return 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256'
+        elif 'AES256-GCM-SHA384' in cipher_without_version and 'ECDHE-ECDSA' in cipher_without_version:
+            return 'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384'
+        elif 'AES128-GCM-SHA256' in cipher_without_version and 'ECDHE-ECDSA' in cipher_without_version:
+            return 'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256'
+        
+        return ns_cipher  # Return as-is if no mapping found
+    
+    def analyze_cipher_compliance(self):
+        """
+        Analyze cipher compliance between IANA ciphers and NS configuration
+        """
+        results = {
+            'configured_ciphers': set(),
+            'configured_in_ns': [],
+            'not_configured': [],
+            'weak_ciphers_found': [],
+            'tls_versions': {},
+            'cipher_group_analysis': [],
+            'ns_cipher_mappings': {}
+        }
+        
+        # Extract all ciphers configured in NS
+        for group in self.ns_config.get('cipher_groups', []):
+            group_ciphers = []
+            for ns_cipher in group['ciphers']:
+                iana_cipher = self.map_ns_cipher_to_iana(ns_cipher)
+                results['configured_ciphers'].add(iana_cipher)
+                results['ns_cipher_mappings'][ns_cipher] = iana_cipher
+                group_ciphers.append({
+                    'ns_name': ns_cipher,
+                    'iana_name': iana_cipher
+                })
+            
+            results['cipher_group_analysis'].append({
+                'group_name': group['name'],
+                'ciphers': group_ciphers,
+                'count': len(group_ciphers)
+            })
+        
+        # Check which IANA ciphers are configured
+        for iana_cipher in self.iana_ciphers:
+            if iana_cipher in results['configured_ciphers']:
+                results['configured_in_ns'].append(iana_cipher)
+            else:
+                results['not_configured'].append(iana_cipher)
+        
+        # Check for weak ciphers
+        weak_patterns = [
+            'RC4', 'DES', '_3DES_', 'MD5', 'NULL', 'EXPORT',
+            'ANON', 'ADH', 'IDEA', 'SEED', 'CAMELLIA'
+        ]
+        
+        for cipher in results['configured_ciphers']:
+            if any(pattern in cipher.upper() for pattern in weak_patterns):
+                results['weak_ciphers_found'].append(cipher)
+        
+        # Check TLS version settings
+        params = self.ns_config.get('ssl_parameters', {})
+        results['tls_versions'] = {
+            'SSL3': params.get('ssl3_enabled', 'NOT_CONFIGURED'),
+            'TLS1.0': params.get('tls1_enabled', 'NOT_CONFIGURED'),
+            'TLS1.1': params.get('tls11_enabled', 'NOT_CONFIGURED'),
+            'TLS1.2': params.get('tls12_enabled', 'NOT_CONFIGURED'),
+            'TLS1.3': params.get('tls13_enabled', 'NOT_CONFIGURED')
+        }
+        
+        return results
+    
+    def generate_report(self):
+        """
+        Generate detailed compliance report
+        """
+        print(f"\n{'='*80}")
+        print(f"CIPHERSCANNER REPORT")
+        print(f"{'='*80}")
+        
+        print(f"\n📊 CipherScanner Summary:")
+        print(f"   IANA Ciphers Found: {len(self.iana_ciphers)}")
+        print(f"   Ciphers Configured in NS: {len(self.analysis_results['configured_ciphers'])}")
+        print(f"   Ciphers Matched: {len(self.analysis_results['configured_in_ns'])}")
+        print(f"   Ciphers Not Configured: {len(self.analysis_results['not_configured'])}")
+        print(f"   Weak Ciphers Found: {len(self.analysis_results['weak_ciphers_found'])}")
+        
+        print(f"\n🔒 TLS Version Configuration:")
+        for version, status in self.analysis_results['tls_versions'].items():
+            status_icon = '✅' if status == 'DISABLED' and version in ['SSL3', 'TLS1.0', 'TLS1.1'] else \
+                         '✅' if status == 'ENABLED' and version in ['TLS1.2', 'TLS1.3'] else \
+                         '⚠️ ' if status == 'ENABLED' and version in ['SSL3', 'TLS1.0', 'TLS1.1'] else \
+                         '❓'
+            print(f"   {status_icon} {version}: {status}")
+        
+        if self.analysis_results['cipher_group_analysis']:
+            print(f"\n📁 Cipher Group Analysis:")
+            for group in self.analysis_results['cipher_group_analysis']:
+                print(f"   • {group['group_name']}: {group['count']} ciphers")
+        
+        if self.analysis_results['configured_in_ns']:
+            print(f"\n✅ Ciphers Configured in NS ({len(self.analysis_results['configured_in_ns'])}):")
+            for cipher in sorted(self.analysis_results['configured_in_ns'])[:20]:
+                print(f"   ✓ {cipher}")
+            if len(self.analysis_results['configured_in_ns']) > 20:
+                print(f"   ... and {len(self.analysis_results['configured_in_ns']) - 20} more")
+        
+        if self.analysis_results['not_configured']:
+            print(f"\n❌ Ciphers NOT Configured in NS ({len(self.analysis_results['not_configured'])}):")
+            for cipher in sorted(self.analysis_results['not_configured'])[:20]:
+                print(f"   ✗ {cipher}")
+            if len(self.analysis_results['not_configured']) > 20:
+                print(f"   ... and {len(self.analysis_results['not_configured']) - 20} more")
+        
+        if self.analysis_results['weak_ciphers_found']:
+            print(f"\n⚠️  WEAK Ciphers Found in NS Configuration ({len(self.analysis_results['weak_ciphers_found'])}):")
+            for cipher in sorted(self.analysis_results['weak_ciphers_found']):
+                print(f"   ⚠️  {cipher}")
+            print("\n   CipherScanner RECOMMENDATION: Remove weak ciphers from configuration")
+        
+        # SSL VServer Summary
+        if self.ns_config.get('ssl_vservers'):
+            print(f"\n🌐 SSL Virtual Servers ({len(self.ns_config['ssl_vservers'])}):")
+            for vs in self.ns_config['ssl_vservers']:
+                cipher_status = "✅" if vs.get('cipher_group') else "❌"
+                print(f"   {cipher_status} {vs['name']} ({vs['ip']}:{vs['port']})")
+                if vs.get('cipher_group'):
+                    print(f"      Cipher Group: {vs['cipher_group']}")
+                if vs.get('ssl_profile'):
+                    print(f"      SSL Profile: {vs['ssl_profile']}")
+        
+        # Security Recommendations
+        print(f"\n📋 CIPHERSCANNER SECURITY RECOMMENDATIONS:")
+        recommendations = []
+        
+        # Check for weak protocols
+        if self.analysis_results['tls_versions'].get('SSL3') == 'ENABLED':
+            recommendations.append("❌ DISABLE SSL 3.0 (Vulnerable to POODLE attack)")
+        if self.analysis_results['tls_versions'].get('TLS1.0') == 'ENABLED':
+            recommendations.append("⚠️  DISABLE TLS 1.0 (Considered weak)")
+        if self.analysis_results['tls_versions'].get('TLS1.1') == 'ENABLED':
+            recommendations.append("⚠️  CONSIDER DISABLING TLS 1.1")
+        
+        if self.analysis_results['weak_ciphers_found']:
+            recommendations.append("❌ REMOVE weak ciphers (RC4, DES, 3DES, MD5)")
+        
+        if len(self.analysis_results['not_configured']) > 0:
+            recommendations.append(f"⚠️  Configure missing {len(self.analysis_results['not_configured'])} cipher(s)")
+        
+        if not recommendations:
+            recommendations.append("✅ CipherScanner: Configuration looks secure!")
+        
+        for i, rec in enumerate(recommendations, 1):
+            print(f"   {i}. {rec}")
+        
+        print(f"\n{'='*80}")
+        print(f"CipherScanner report generated at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
+        print(f"{'='*80}")
+    
+    def save_json_report(self, output_file):
+        """Save comprehensive report as JSON"""
+        report = {
+            'metadata': {
+                'generated_at': datetime.now().isoformat(),
+                'tool': 'CipherScanner',
+                'version': '1.0.0',
+                'iana_cipher_count': len(self.iana_ciphers),
+                'ns_config_file': self.ns_conf_file,
+                'cipher_file': self.cipher_file
+            },
+            'iana_ciphers': self.iana_ciphers,
+            'ns_configuration_summary': {
+                'cipher_groups_count': len(self.ns_config.get('cipher_groups', [])),
+                'ssl_vservers_count': len(self.ns_config.get('ssl_vservers', [])),
+                'ssl_profiles_count': len(self.ns_config.get('ssl_profiles', [])),
+                'tls_versions': self.ns_config.get('ssl_parameters', {})
+            },
+            'compliance_analysis': {
+                'configured_ciphers': list(self.analysis_results['configured_ciphers']),
+                'configured_in_ns': self.analysis_results['configured_in_ns'],
+                'not_configured': self.analysis_results['not_configured'],
+                'weak_ciphers_found': self.analysis_results['weak_ciphers_found'],
+                'tls_versions': self.analysis_results['tls_versions'],
+                'ns_cipher_mappings': self.analysis_results['ns_cipher_mappings']
+            },
+            'security_assessment': {
+                'has_weak_ciphers': len(self.analysis_results['weak_ciphers_found']) > 0,
+                'weak_cipher_count': len(self.analysis_results['weak_ciphers_found']),
+                'tls1_2_enabled': self.analysis_results['tls_versions'].get('TLS1.2') == 'ENABLED',
+                'tls1_3_enabled': self.analysis_results['tls_versions'].get('TLS1.3') == 'ENABLED',
+                'ssl3_disabled': self.analysis_results['tls_versions'].get('SSL3') == 'DISABLED',
+                'overall_grade': 'A' if len(self.analysis_results['weak_ciphers_found']) == 0 else 'C'
+            },
+            'cipher_scanner_info': {
+                'scan_completed': True,
+                'scan_timestamp': datetime.now().isoformat(),
+                'exit_code': self.get_exit_code()
+            }
+        }
+        
+        with open(output_file, 'w') as f:
+            json.dump(report, f, indent=2)
+        
+        print(f"\n💾 CipherScanner JSON report saved to: {output_file}")
+        return report
+    
+    def get_exit_code(self):
+        """Determine exit code based on scan results"""
+        if self.analysis_results['weak_ciphers_found']:
+            return 2  # Failure: weak ciphers detected
+        elif len(self.analysis_results['not_configured']) > len(self.iana_ciphers) * 0.5:
+            return 1  # Warning: less than 50% of ciphers configured
+        else:
+            return 0  # Success
+
+def main():
+    parser = argparse.ArgumentParser(
+        description='CipherScanner - NS Configuration Cipher Suite Compliance Checker',
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        epilog="""
+Examples:
+  %(prog)s --ns-conf ns.conf --ciphers ciphers.txt
+  %(prog)s --ns-conf /path/to/ns.conf --ciphers scan_results.txt --output report.json
+  %(prog)s --ns-conf ns.conf --ciphers ciphers.txt --verbose
+  
+CipherScanner helps security teams audit NS load balancer cipher configurations
+against discovered cipher suites from security scans.
+        """
+    )
+    
+    parser.add_argument('--nsconf', required=True, help='NS configuration file (ns.conf)')
+    parser.add_argument('--cipher', required=True, help='Cipher suites text file')
+    parser.add_argument('--output', help='Output JSON report file')
+    parser.add_argument('--verbose', action='store_true', help='Show detailed parsing information')
+    
+    args = parser.parse_args()
+    
+    # Initialize and run CipherScanner
+    scanner = CipherScanner(args.nsconf, args.cipher)
+    
+    if scanner.run_scan():
+        # Save JSON report if requested
+        if args.output:
+            scanner.save_json_report(args.output)
+        
+        # Exit with appropriate code
+        exit_code = scanner.get_exit_code()
+        
+        if exit_code == 0:
+            print(f"\n✅ CipherScanner: Compliance PASS - Configuration looks good!")
+        elif exit_code == 1:
+            print(f"\n⚠️  CipherScanner: Warning - Less than 50% of scanned ciphers are configured")
+        else:
+            print(f"\n❌ CipherScanner: Compliance FAILURE - Weak ciphers detected!")
+        
+        sys.exit(exit_code)
+    else:
+        print(f"\n❌ CipherScanner: Scan failed due to errors")
+        sys.exit(3)
+
+if __name__ == "__main__":
+    main()