File size: 7,081 Bytes
184b62a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
"""
Validators Module - Input validation and sanitization
Ensures all user inputs are safe and valid
"""

import re
from typing import Dict, List, Tuple, Optional

class InputValidator:
    """Validates user inputs for MVP Agent"""
    
    # Constants
    MIN_IDEA_LENGTH = 10
    MAX_IDEA_LENGTH = 1000
    DANGEROUS_PATTERNS = [
        r'<script',
        r'javascript:',
        r'onerror=',
        r'onclick=',
        r'eval\(',
        r'exec\(',
    ]
    
    @staticmethod
    def validate_startup_idea(idea: str) -> Tuple[bool, Optional[str]]:
        """
        Validate startup idea input
        
        Args:
            idea: The startup idea string
            
        Returns:
            Tuple of (is_valid, error_message)
        """
        # Check if empty
        if not idea:
            return False, "Please enter a startup idea"
        
        # Check type
        if not isinstance(idea, str):
            return False, "Idea must be text"
        
        # Strip whitespace
        idea = idea.strip()
        
        # Check length
        if len(idea) < InputValidator.MIN_IDEA_LENGTH:
            return False, f"Idea too short. Please enter at least {InputValidator.MIN_IDEA_LENGTH} characters"
        
        if len(idea) > InputValidator.MAX_IDEA_LENGTH:
            return False, f"Idea too long. Please keep it under {InputValidator.MAX_IDEA_LENGTH} characters"
        
        # Check for dangerous patterns (basic XSS prevention)
        for pattern in InputValidator.DANGEROUS_PATTERNS:
            if re.search(pattern, idea, re.IGNORECASE):
                return False, "Invalid characters detected in idea"
        
        # Check if meaningful (not just spaces or special chars)
        if not re.search(r'[a-zA-Z]', idea):
            return False, "Please enter a meaningful idea with text"
        
        return True, None
    
    @staticmethod
    def sanitize_idea(idea: str) -> str:
        """
        Sanitize startup idea for safe processing
        
        Args:
            idea: The raw idea
            
        Returns:
            Sanitized idea string
        """
        # Strip whitespace
        idea = idea.strip()
        
        # Remove control characters
        idea = ''.join(char for char in idea if ord(char) >= 32 or char in '\n\r\t')
        
        # Limit consecutive spaces
        idea = re.sub(r'\s+', ' ', idea)
        
        # Remove HTML-like tags
        idea = re.sub(r'<[^>]+>', '', idea)
        
        return idea
    
    @staticmethod
    def validate_api_key(api_key: str, key_name: str = "API key") -> Tuple[bool, Optional[str]]:
        """
        Validate API key format
        
        Args:
            api_key: The API key to validate
            key_name: Name of the key (for error messages)
            
        Returns:
            Tuple of (is_valid, error_message)
        """
        if not api_key:
            return False, f"{key_name} is required"
        
        if not isinstance(api_key, str):
            return False, f"{key_name} must be a string"
        
        api_key = api_key.strip()
        
        if len(api_key) < 10:
            return False, f"{key_name} appears to be invalid (too short)"
        
        # Check for obvious placeholder values
        placeholder_values = [
            'your_api_key',
            'insert_key_here',
            'api_key_here',
            'placeholder',
            'xxxxxx'
        ]
        
        if api_key.lower() in placeholder_values:
            return False, f"Please replace the placeholder {key_name}"
        
        return True, None
    
    @staticmethod
    def validate_file_path(path: str) -> Tuple[bool, Optional[str]]:
        """
        Validate file path for safety
        
        Args:
            path: File path to validate
            
        Returns:
            Tuple of (is_valid, error_message)
        """
        if not path:
            return False, "File path cannot be empty"
        
        # Check for path traversal attempts
        if '..' in path:
            return False, "Invalid file path (path traversal detected)"
        
        # Check for absolute paths (we want relative only)
        if path.startswith('/') or (len(path) > 1 and path[1] == ':'):
            return False, "Only relative paths are allowed"
        
        # Check for dangerous characters
        dangerous_chars = ['<', '>', '|', '\0', '\n', '\r']
        if any(char in path for char in dangerous_chars):
            return False, "Invalid characters in file path"
        
        return True, None

class OutputValidator:
    """Validates agent outputs"""
    
    @staticmethod
    def validate_mvp_files(files: Dict[str, str]) -> Tuple[bool, List[str]]:
        """
        Validate generated MVP files
        
        Args:
            files: Dictionary of file contents
            
        Returns:
            Tuple of (is_valid, list_of_errors)
        """
        errors = []
        required_files = [
            'features_md',
            'architecture_md',
            'design_md',
            'user_flow_md',
            'roadmap_md'
        ]
        
        # Check all required files present
        for file_key in required_files:
            if file_key not in files:
                errors.append(f"Missing required file: {file_key}")
            elif not files[file_key]:
                errors.append(f"Empty file: {file_key}")
            elif len(files[file_key]) < 100:
                errors.append(f"File too short (< 100 chars): {file_key}")
        
        # Check for markdown headers
        for file_key, content in files.items():
            if file_key in required_files:
                if not content.strip().startswith('#'):
                    errors.append(f"File missing markdown header: {file_key}")
        
        return len(errors) == 0, errors
    
    @staticmethod
    def validate_json_structure(data: dict, required_keys: List[str]) -> Tuple[bool, List[str]]:
        """
        Validate JSON structure has required keys
        
        Args:
            data: JSON data as dictionary
            required_keys: List of required keys
            
        Returns:
            Tuple of (is_valid, list_of_missing_keys)
        """
        missing_keys = []
        
        for key in required_keys:
            if key not in data:
                missing_keys.append(key)
            elif data[key] is None:
                missing_keys.append(f"{key} (null value)")
        
        return len(missing_keys) == 0, missing_keys

# Convenience functions
def validate_idea(idea: str) -> Tuple[bool, Optional[str]]:
    """Quick validation of startup idea"""
    return InputValidator.validate_startup_idea(idea)

def sanitize_idea(idea: str) -> str:
    """Quick sanitization of startup idea"""
    return InputValidator.sanitize_idea(idea)

def validate_files(files: Dict[str, str]) -> Tuple[bool, List[str]]:
    """Quick validation of MVP files"""
    return OutputValidator.validate_mvp_files(files)