diff --git a/app.py b/app.py index 6a541ed..0b08ebe 100644 --- a/app.py +++ b/app.py @@ -138,6 +138,14 @@ def is_allowed(self, identifier: str, max_requests: int, window_seconds: int) -> self._requests[identifier].append(now) return True + def remaining(self, identifier: str, max_requests: int, window_seconds: int) -> int: + with self._lock: + now = time.time() + window_start = now - window_seconds + while self._requests[identifier] and self._requests[identifier][0] < window_start: + self._requests[identifier].popleft() + return max_requests - len(self._requests[identifier]) + # Initialize global components metrics = ApplicationMetrics() rate_limiter = RateLimiter() @@ -158,17 +166,28 @@ def decorated_function(*args, **kwargs): client_ip = get_client_ip() if not rate_limiter.is_allowed( - client_ip, - app.config['RATE_LIMIT_MAX_REQUESTS'], + client_ip, + app.config['RATE_LIMIT_MAX_REQUESTS'], app.config['RATE_LIMIT_WINDOW'] ): logger.warning(f"Rate limit exceeded for IP: {client_ip}") - return jsonify({ + resp = jsonify({ 'error': 'Rate limit exceeded', 'message': f'Maximum {app.config["RATE_LIMIT_MAX_REQUESTS"]} requests per hour allowed' - }), 429 - - return f(*args, **kwargs) + }) + resp.status_code = 429 + resp.headers['Retry-After'] = str(app.config['RATE_LIMIT_WINDOW']) + return resp + + response = f(*args, **kwargs) + if hasattr(response, 'headers'): + remaining = rate_limiter.remaining( + client_ip, + app.config['RATE_LIMIT_MAX_REQUESTS'], + app.config['RATE_LIMIT_WINDOW'] + ) + response.headers['X-RateLimit-Remaining'] = str(remaining) + return response return decorated_function def security_headers(f): @@ -291,10 +310,13 @@ def too_large(e): def ratelimit_handler(e): """Handle rate limit exceeded""" metrics.record_error() - return jsonify({ + resp = jsonify({ 'error': 'Rate limit exceeded', 'message': 'Too many requests. Please try again later.' - }), 429 + }) + resp.status_code = 429 + resp.headers['Retry-After'] = str(app.config['RATE_LIMIT_WINDOW']) + return resp @app.errorhandler(500) def internal_error(e): diff --git a/scanner.py b/scanner.py index 45eb7fb..ea6c60d 100644 --- a/scanner.py +++ b/scanner.py @@ -231,6 +231,17 @@ def _initialize_advanced_patterns(self) -> Dict[str, List[Dict]]: 'cwe_id': 'CWE-295', 'recommendation': 'Always verify SSL certificates or use custom CA bundle if needed', 'category': 'crypto' + }, + { + 'pattern': r'requests\.(get|post|put|delete)\s*\([^)]*[\'\"]http://', + 'issue': 'Insecure HTTP Request', + 'severity': 'Medium', + 'description': 'HTTP used instead of HTTPS for network request', + 'confidence': 0.7, + 'owasp_category': 'A08:2021-Software and Data Integrity Failures', + 'cwe_id': 'CWE-319', + 'recommendation': 'Use HTTPS and verify certificates for all requests', + 'category': 'insecure_transport' } ], @@ -317,6 +328,17 @@ def _initialize_advanced_patterns(self) -> Dict[str, List[Dict]]: 'recommendation': 'Use crypto.getRandomValues() for cryptographic random numbers', 'category': 'crypto' }, + { + 'pattern': r'(fetch|axios\.get)\s*\([^)]*[\'\"]http://', + 'issue': 'Insecure HTTP Request', + 'severity': 'Medium', + 'description': 'HTTP used instead of HTTPS for network request', + 'confidence': 0.7, + 'owasp_category': 'A08:2021-Software and Data Integrity Failures', + 'cwe_id': 'CWE-319', + 'recommendation': 'Use HTTPS for all remote requests', + 'category': 'insecure_transport' + }, # URL Manipulation { @@ -355,6 +377,17 @@ def _initialize_advanced_patterns(self) -> Dict[str, List[Dict]]: 'cwe_id': 'CWE-20', 'recommendation': 'Use proper TypeScript types and input validation instead of any', 'category': 'type_safety' + }, + { + 'pattern': r'(fetch|axios\.get)\s*\([^)]*[\'\"]http://', + 'issue': 'Insecure HTTP Request', + 'severity': 'Medium', + 'description': 'HTTP used instead of HTTPS for network request', + 'confidence': 0.7, + 'owasp_category': 'A08:2021-Software and Data Integrity Failures', + 'cwe_id': 'CWE-319', + 'recommendation': 'Use HTTPS for all remote requests', + 'category': 'insecure_transport' } ], diff --git a/templates/index.html b/templates/index.html index 5c6d5c5..bf23524 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1528,6 +1528,12 @@