#!/usr/bin/env python3 """ Vereinfachter HTML-Formular Tester für MYP System =============================================== Ein schlanker Formular-Tester ohne externe Dependencies, der grundlegende Formular-Validierung mit Standard-Python-Libraries testet. Autor: Till Tomczak Mercedes-Benz Projektarbeit MYP """ import asyncio import json import time import urllib.request import urllib.parse import urllib.error from urllib.parse import urljoin, urlparse from html.parser import HTMLParser from dataclasses import dataclass from typing import Dict, List, Optional import sys import re @dataclass class FormField: """Repräsentiert ein Formular-Feld""" name: str field_type: str required: bool = False pattern: str = "" placeholder: str = "" value: str = "" @dataclass class Form: """Repräsentiert ein HTML-Formular""" action: str method: str fields: List[FormField] name: str = "" class FormParser(HTMLParser): """Parst HTML und extrahiert Formular-Informationen""" def __init__(self): super().__init__() self.forms = [] self.current_form = None self.in_form = False def handle_starttag(self, tag, attrs): attrs_dict = dict(attrs) if tag == 'form': self.in_form = True self.current_form = Form( action=attrs_dict.get('action', ''), method=attrs_dict.get('method', 'GET').upper(), fields=[], name=attrs_dict.get('name', attrs_dict.get('id', f'form_{len(self.forms)}')) ) elif self.in_form and tag in ['input', 'textarea', 'select']: field = FormField( name=attrs_dict.get('name', attrs_dict.get('id', f'field_{len(self.current_form.fields)}')), field_type=attrs_dict.get('type', tag), required='required' in attrs_dict, pattern=attrs_dict.get('pattern', ''), placeholder=attrs_dict.get('placeholder', ''), value=attrs_dict.get('value', '') ) self.current_form.fields.append(field) def handle_endtag(self, tag): if tag == 'form' and self.in_form: self.in_form = False if self.current_form: self.forms.append(self.current_form) self.current_form = None class SimpleFormTester: """ Vereinfachter Formular-Tester ohne Browser-Dependencies. Führt HTTP-basierte Tests durch. """ def __init__(self, base_url: str): self.base_url = base_url.rstrip('/') self.session_cookies = {} def fetch_page(self, path: str) -> str: """Lädt eine Seite und gibt den HTML-Inhalt zurück""" url = urljoin(self.base_url, path) try: request = urllib.request.Request(url) # Cookies hinzufügen if self.session_cookies: cookie_header = '; '.join([f'{k}={v}' for k, v in self.session_cookies.items()]) request.add_header('Cookie', cookie_header) with urllib.request.urlopen(request) as response: html = response.read().decode('utf-8') # Cookies aus Response extrahieren cookie_header = response.getheader('Set-Cookie') if cookie_header: self._parse_cookies(cookie_header) return html except urllib.error.URLError as e: print(f"❌ Fehler beim Laden von {url}: {e}") return "" def _parse_cookies(self, cookie_header: str): """Parst Set-Cookie Header""" for cookie in cookie_header.split(','): if '=' in cookie: name, value = cookie.split('=', 1) self.session_cookies[name.strip()] = value.split(';')[0].strip() def find_forms(self, html: str) -> List[Form]: """Findet alle Formulare in HTML""" parser = FormParser() parser.feed(html) return parser.forms def generate_test_data(self, field: FormField) -> str: """Generiert Test-Daten für ein Feld""" if field.field_type == 'email': return "test@mercedes-benz.com" elif field.field_type == 'password': return "TestPassword123!" elif field.field_type == 'tel': return "+49 711 17-0" elif field.field_type == 'url': return "https://www.mercedes-benz.com" elif field.field_type == 'number': return "42" elif field.field_type == 'date': return "2024-06-18" elif field.field_type == 'checkbox': return "on" elif field.field_type == 'radio': return field.value or "option1" elif 'name' in field.name.lower(): return "Test Benutzer" elif 'username' in field.name.lower(): return "admin" elif 'ip' in field.name.lower(): return "192.168.1.100" elif 'port' in field.name.lower(): return "80" else: return f"Test_{field.name}" def submit_form(self, form: Form, test_data: Dict[str, str], base_url: str) -> Dict: """Sendet Formular-Daten und gibt Response zurück""" # Action URL bestimmen if form.action.startswith('http'): url = form.action else: url = urljoin(base_url, form.action) # Daten vorbereiten form_data = {} for field in form.fields: if field.name in test_data: form_data[field.name] = test_data[field.name] elif field.field_type not in ['submit', 'button']: form_data[field.name] = self.generate_test_data(field) try: if form.method == 'GET': # GET-Request mit Query-Parametern query_string = urllib.parse.urlencode(form_data) full_url = f"{url}?{query_string}" request = urllib.request.Request(full_url) else: # POST-Request data = urllib.parse.urlencode(form_data).encode('utf-8') request = urllib.request.Request(url, data=data) request.add_header('Content-Type', 'application/x-www-form-urlencoded') # Cookies hinzufügen if self.session_cookies: cookie_header = '; '.join([f'{k}={v}' for k, v in self.session_cookies.items()]) request.add_header('Cookie', cookie_header) with urllib.request.urlopen(request) as response: response_html = response.read().decode('utf-8') status_code = response.getcode() return { 'success': 200 <= status_code < 400, 'status_code': status_code, 'html': response_html, 'form_data': form_data } except urllib.error.HTTPError as e: return { 'success': False, 'status_code': e.code, 'error': str(e), 'form_data': form_data } except Exception as e: return { 'success': False, 'error': str(e), 'form_data': form_data } def validate_field(self, field: FormField, value: str) -> Dict: """Validiert ein Feld mit gegebenem Wert""" errors = [] # Required-Validierung if field.required and not value.strip(): errors.append(f"Feld '{field.name}' ist erforderlich") # Pattern-Validierung if field.pattern and value: try: if not re.match(field.pattern, value): errors.append(f"Feld '{field.name}' entspricht nicht dem Pattern '{field.pattern}'") except re.error: errors.append(f"Ungültiges Pattern für Feld '{field.name}': {field.pattern}") # Type-spezifische Validierung if value and field.field_type == 'email': email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' if not re.match(email_pattern, value): errors.append(f"Ungültige Email-Adresse: {value}") elif value and field.field_type == 'url': try: parsed = urlparse(value) if not parsed.scheme or not parsed.netloc: errors.append(f"Ungültige URL: {value}") except: errors.append(f"Ungültige URL: {value}") elif value and field.field_type == 'number': try: float(value) except ValueError: errors.append(f"'{value}' ist keine gültige Zahl") return { 'valid': len(errors) == 0, 'errors': errors, 'field': field.name, 'value': value } def test_form_validation(self, form: Form) -> List[Dict]: """Testet Formular-Validierung mit verschiedenen Daten""" validation_results = [] # 1. Test mit leeren Required-Feldern for field in form.fields: if field.required: result = self.validate_field(field, "") validation_results.append({ 'test_type': 'required_field_empty', 'field': field.name, 'expected_invalid': True, 'actual_valid': result['valid'], 'passed': not result['valid'], # Sollte ungültig sein 'errors': result['errors'] }) # 2. Test mit ungültigen Email-Adressen for field in form.fields: if field.field_type == 'email': invalid_emails = ['invalid-email', 'test@', '@domain.com', 'test..test@domain.com'] for invalid_email in invalid_emails: result = self.validate_field(field, invalid_email) validation_results.append({ 'test_type': 'invalid_email', 'field': field.name, 'value': invalid_email, 'expected_invalid': True, 'actual_valid': result['valid'], 'passed': not result['valid'], 'errors': result['errors'] }) # 3. Test mit gültigen Daten valid_test_data = {} for field in form.fields: if field.field_type not in ['submit', 'button']: valid_test_data[field.name] = self.generate_test_data(field) for field in form.fields: if field.name in valid_test_data: result = self.validate_field(field, valid_test_data[field.name]) validation_results.append({ 'test_type': 'valid_data', 'field': field.name, 'value': valid_test_data[field.name], 'expected_invalid': False, 'actual_valid': result['valid'], 'passed': result['valid'], 'errors': result['errors'] }) return validation_results def test_form_submission(self, form: Form, base_url: str) -> Dict: """Testet Formular-Submission""" print(f"🧪 Teste Formular: {form.name} ({form.method} → {form.action})") # Test-Daten generieren test_data = {} for field in form.fields: if field.field_type not in ['submit', 'button']: test_data[field.name] = self.generate_test_data(field) print(f" 📝 Test-Daten: {list(test_data.keys())}") # Formular senden start_time = time.time() result = self.submit_form(form, test_data, base_url) execution_time = time.time() - start_time # Validierungs-Tests validation_results = self.test_form_validation(form) return { 'form_name': form.name, 'method': form.method, 'action': form.action, 'fields_count': len(form.fields), 'test_data': test_data, 'submission_result': result, 'validation_results': validation_results, 'execution_time': execution_time, 'timestamp': time.strftime('%Y-%m-%d %H:%M:%S') } def test_page_forms(self, path: str) -> List[Dict]: """Testet alle Formulare auf einer Seite""" print(f"🔍 Scanne Formulare auf: {self.base_url}{path}") # Seite laden html = self.fetch_page(path) if not html: return [] # Formulare finden forms = self.find_forms(html) print(f"✅ {len(forms)} Formulare gefunden") # Jedes Formular testen results = [] for form in forms: result = self.test_form_submission(form, self.base_url + path) results.append(result) return results def generate_report(self, test_results: List[Dict], output_file: str = "simple_form_test_report.html"): """Generiert einen einfachen HTML-Report""" total_forms = len(test_results) successful_submissions = len([r for r in test_results if r['submission_result'].get('success', False)]) # Validierungs-Statistiken total_validations = sum(len(r['validation_results']) for r in test_results) passed_validations = sum(len([v for v in r['validation_results'] if v['passed']]) for r in test_results) html_report = f""" MYP Formular Test Report

🧪 MYP Formular Test Report

Generiert am {time.strftime('%d.%m.%Y um %H:%M:%S')}

{total_forms}
Formulare getestet
{successful_submissions}
Erfolgreiche Submissions
{total_validations}
Validierungs-Tests
{passed_validations}
Validierungen bestanden

📋 Detaillierte Ergebnisse

""" # Formular-spezifische Ergebnisse for result in test_results: submission = result['submission_result'] success_class = 'success' if submission.get('success', False) else 'failure' html_report += f"""

📝 {result['form_name']}

Method: {result['method']} | Action: {result['action']} | Felder: {result['fields_count']}

Submission-Ergebnis: {'✅ Erfolgreich' if submission.get('success', False) else '❌ Fehlgeschlagen'}

{f'

Status Code: {submission.get("status_code", "N/A")}

' if 'status_code' in submission else ''} {f'

Fehler: {submission.get("error", "N/A")}

' if 'error' in submission else ''}

Test-Daten:

{json.dumps(result['test_data'], indent=2, ensure_ascii=False)}

Validierungs-Ergebnisse:

""" # Validierungs-Ergebnisse for validation in result['validation_results']: validation_class = 'validation-passed' if validation['passed'] else 'validation-failed' status_icon = '✅' if validation['passed'] else '❌' html_report += f"""
{status_icon} {validation['test_type']} - Feld: {validation['field']} {f"
Wert: {validation.get('value', 'N/A')}" if 'value' in validation else ''} {f"
Fehler: {', '.join(validation['errors'])}" if validation['errors'] else ''}
""" html_report += f"""

⏱️ Ausführungszeit: {result['execution_time']:.2f}s

""" html_report += """
""" try: with open(output_file, 'w', encoding='utf-8') as f: f.write(html_report) print(f"📊 Report erstellt: {output_file}") return output_file except Exception as e: print(f"❌ Fehler beim Report-Erstellen: {e}") return "" def main(): """Haupt-CLI-Interface""" if len(sys.argv) < 2: print(""" 🧪 Vereinfachter MYP Formular Tester Verwendung: python simple_form_tester.py [path] Beispiele: python simple_form_tester.py http://localhost:5000 python simple_form_tester.py http://localhost:5000 /login python simple_form_tester.py http://localhost:5000 /admin/add_printer Testet alle Formulare auf der angegebenen Seite und generiert einen HTML-Report. """) return base_url = sys.argv[1] path = sys.argv[2] if len(sys.argv) > 2 else '/' print(f""" {'='*60} 🧪 MYP FORMULAR TESTER (Vereinfacht) {'='*60} 🎯 Basis-URL: {base_url} 📍 Pfad: {path} 🕒 Gestartet: {time.strftime('%d.%m.%Y um %H:%M:%S')} {'='*60} """) # Tester initialisieren tester = SimpleFormTester(base_url) # Tests ausführen try: results = tester.test_page_forms(path) if results: # Report generieren report_file = tester.generate_report(results) # Zusammenfassung total_forms = len(results) successful = len([r for r in results if r['submission_result'].get('success', False)]) print(f""" {'='*60} 🎉 TESTS ABGESCHLOSSEN {'='*60} 📊 ZUSAMMENFASSUNG: • Formulare getestet: {total_forms} • Erfolgreiche Submissions: {successful} • Erfolgsrate: {successful/total_forms*100:.1f}% 📋 Report: {report_file} {'='*60} """) else: print("⚠️ Keine Formulare gefunden oder Fehler beim Laden der Seite") except Exception as e: print(f"❌ Fehler beim Testen: {e}") import traceback traceback.print_exc() if __name__ == "__main__": main()