Skip to content

Commit fe0fa38

Browse files
committed
Add comprehensive input validation system for security
This commit implements a complete input validation framework to prevent security vulnerabilities and improve application stability: Security Features: - MFT file validation with magic number checking - Path traversal protection for output files - Numeric bounds checking to prevent resource exhaustion - Buffer overflow prevention in MFT record parsing - Configuration schema validation against injection attacks - Export format validation with dependency checking Key Components: - New validators.py module with comprehensive validation functions - Integration into CLI with proper error handling and user feedback - Buffer overflow protection in MFT record attribute parsing - Configuration validation in config.py - Comprehensive test suite with 36 test cases covering all scenarios Addresses Issue #165: Add comprehensive input validation
1 parent c3611bb commit fe0fa38

File tree

8 files changed

+1709
-2
lines changed

8 files changed

+1709
-2
lines changed

src/analyzeMFT/cli.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
from .constants import VERSION
88
from .config import ConfigManager, find_config_file
99
from .test_generator import create_test_mft
10+
from .validators import (
11+
validate_paths_secure, validate_numeric_bounds, validate_export_format,
12+
validate_config_schema, ValidationError, MFTValidationError,
13+
PathValidationError, NumericValidationError, ConfigValidationError
14+
)
1015

1116
async def main():
1217
# Setup basic logging configuration (will be refined by analyzer)
@@ -208,6 +213,40 @@ async def main():
208213
if not options.export_format:
209214
options.export_format = "csv"
210215

216+
# ========== INPUT VALIDATION ==========
217+
# Comprehensive validation of all inputs before processing
218+
try:
219+
# Validate numeric parameters
220+
validate_numeric_bounds(
221+
chunk_size=options.chunk_size,
222+
hash_processes=options.hash_processes,
223+
test_records=options.test_records,
224+
verbosity=options.verbosity,
225+
debug=options.debug
226+
)
227+
228+
# Validate export format and dependencies
229+
validate_export_format(options.export_format, options.output_file)
230+
231+
# Validate and secure file paths
232+
validated_input, validated_output = validate_paths_secure(
233+
options.filename, options.output_file
234+
)
235+
236+
# Update options with validated paths
237+
options.filename = str(validated_input)
238+
options.output_file = str(validated_output)
239+
240+
logging.info("All input validation checks passed successfully")
241+
242+
except ValidationError as e:
243+
logging.error(f"Validation Error: {e}")
244+
sys.exit(1)
245+
except Exception as e:
246+
logging.error(f"Unexpected error during validation: {e}")
247+
sys.exit(1)
248+
# ========== END INPUT VALIDATION ==========
249+
211250
try:
212251
analyzer = MftAnalyzer(
213252
options.filename,

src/analyzeMFT/config.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from pathlib import Path
55
from typing import Dict, Any, Optional, Union
66
from dataclasses import dataclass, asdict
7+
from .validators import validate_config_schema, ConfigValidationError
78

89
try:
910
import yaml
@@ -137,11 +138,19 @@ def load_config_file(self, config_path: Union[str, Path]) -> Dict[str, Any]:
137138

138139
def load_profile_from_config(self, config: Dict[str, Any], profile_name: str = "custom") -> AnalysisProfile:
139140
"""Create an AnalysisProfile from configuration dictionary"""
141+
# Validate configuration schema first
142+
try:
143+
validated_config = validate_config_schema(config)
144+
self.logger.info(f"Configuration validation successful for profile '{profile_name}'")
145+
except ConfigValidationError as e:
146+
self.logger.error(f"Configuration validation failed for profile '{profile_name}': {e}")
147+
raise
148+
140149
# Start with default profile
141150
profile_data = asdict(self.profiles['default'])
142151

143-
# Update with configuration values
144-
profile_data.update(config)
152+
# Update with validated configuration values
153+
profile_data.update(validated_config)
145154
profile_data['name'] = profile_name
146155

147156
return AnalysisProfile(**profile_data)

src/analyzeMFT/mft_record.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import logging
66
from .constants import *
77
from .windows_time import WindowsTime
8+
from .validators import validate_attribute_length, ValidationError
89

910
from typing import Dict, Set, List, Optional, Any, Union
1011

@@ -127,6 +128,20 @@ def parse_attributes(self):
127128
self.log("End of attributes reached", 3)
128129
break
129130

131+
# Validate attribute length to prevent buffer overruns
132+
try:
133+
validate_attribute_length(
134+
attr_len=attr_len,
135+
offset=offset,
136+
record_size=len(self.raw_record),
137+
attr_type=attr_type
138+
)
139+
except ValidationError as e:
140+
self.logger.error(f"Attribute validation failed at record {getattr(self, 'recordnum', 'unknown')}: {e}")
141+
# Continue parsing other attributes rather than failing completely
142+
offset += 8 # Skip this attribute header
143+
continue
144+
130145
self.attribute_types.add(attr_type)
131146

132147
if attr_type == STANDARD_INFORMATION_ATTRIBUTE:

0 commit comments

Comments
 (0)