dotscope 0.6.0

A high-performance, cross-platform framework for analyzing and reverse engineering .NET PE executables
Documentation
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
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
//! Table structure and metadata table integrity validation for .NET assemblies.
//!
//! This validator ensures the fundamental integrity of metadata table structures,
//! including proper table headers, row counts, and cross-table dependencies.
//! It operates on raw metadata structures to validate the foundational requirements
//! before higher-level semantic validation can proceed. This validator runs with
//! priority 190 in the raw validation stage, after token validation but before
//! constraint validation.
//!
//! # Architecture
//!
//! The table validation system implements comprehensive table-level validation strategies in sequential order:
//! 1. **Required Table Presence** - Ensures essential tables are present in valid assemblies (Module, Assembly)
//! 2. **Table Structure Validation** - Ensures table headers and row counts are consistent with ECMA-335 requirements
//! 3. **Cross-table Dependencies** - Validates relationships between metadata tables (TypeDef-Field, TypeDef-Method lists)
//!
//! The implementation uses type-safe dispatch mechanisms via [`crate::dispatch_table_type`] to validate all metadata
//! table types, ensuring comprehensive coverage of table structures according to ECMA-335 specifications.
//! This validator provides essential structural guarantees that higher-level validators depend upon.
//!
//! # Key Components
//!
//! - [`crate::metadata::validation::validators::raw::structure::table::RawTableValidator`] - Main validator implementation providing comprehensive table structure validation
//! - [`crate::metadata::validation::validators::raw::structure::table::RawTableValidator::validate_required_tables`] - Essential table presence validation for Module and Assembly tables
//! - [`crate::metadata::validation::validators::raw::structure::table::RawTableValidator::validate_table_structures`] - Core table structure validation including row count limits
//! - [`crate::metadata::validation::validators::raw::structure::table::RawTableValidator::validate_table_dependencies`] - Cross-table relationship validation for list-based references
//!
//! # Usage Examples
//!
//! ```rust,no_run
//! use dotscope::metadata::validation::{RawTableValidator, RawValidator, RawValidationContext};
//!
//! # fn get_context() -> RawValidationContext<'static> { unimplemented!() }
//! let context = get_context();
//! let validator = RawTableValidator::new();
//!
//! // Check if validation should run based on configuration
//! if validator.should_run(&context) {
//!     validator.validate_raw(&context)?;
//! }
//! # Ok::<(), dotscope::Error>(())
//! ```
//!
//! # Error Handling
//!
//! This validator returns [`crate::Error::ValidationRawFailed`] for:
//! - Missing required metadata tables (Module table always required, Assembly table for executables)
//! - Invalid table row counts or inconsistent table headers (row counts exceeding 0x00FFFFFF)
//! - Malformed table structures or corrupted metadata (RID inconsistencies within tables)
//! - Cross-table dependency violations (TypeDef field/method list references beyond table bounds)
//! - Assembly table containing more than one row (ECMA-335 violation)
//! - Module table containing zero rows (at least one Module entry required)
//!
//! # Thread Safety
//!
//! All validation operations are read-only and thread-safe. The validator implements [`Send`] + [`Sync`]
//! and can be used concurrently across multiple threads without synchronization as it operates on
//! immutable metadata structures.
//!
//! # Integration
//!
//! This validator integrates with:
//! - raw structure validators - Part of the foundational structural validation stage
//! - [`crate::metadata::validation::engine::ValidationEngine`] - Orchestrates validator execution with fail-fast behavior
//! - [`crate::metadata::validation::traits::RawValidator`] - Implements the raw validation interface
//! - [`crate::metadata::cilassemblyview::CilAssemblyView`] - Source of metadata tables for validation
//! - [`crate::metadata::validation::context::RawValidationContext`] - Provides validation execution context
//! - [`crate::metadata::validation::config::ValidationConfig`] - Controls validation execution via enable_structural_validation flag
//!
//! # References
//!
//! - [ECMA-335 II.22](https://ecma-international.org/wp-content/uploads/ECMA-335_6th_edition_june_2012.pdf) - Metadata tables specification
//! - [ECMA-335 II.24.2](https://ecma-international.org/wp-content/uploads/ECMA-335_6th_edition_june_2012.pdf) - Metadata layout requirements

use crate::{
    dispatch_table_type,
    metadata::{
        cilassemblyview::CilAssemblyView,
        tables::{AssemblyRaw, FieldRaw, MethodDefRaw, ModuleRaw, TableId, TypeDefRaw},
        validation::{
            context::{RawValidationContext, ValidationContext},
            traits::RawValidator,
        },
    },
    Result,
};
use strum::IntoEnumIterator;

/// Foundation validator for metadata table structure and integrity.
///
/// Ensures the structural integrity of all metadata tables in a .NET assembly,
/// validating table headers, row counts, required table presence, and cross-table
/// dependencies. This validator operates at the lowest level of metadata validation,
/// providing essential guarantees before higher-level semantic validation can proceed.
///
/// The validator implements comprehensive coverage of all metadata table types using
/// type-safe dispatch mechanisms and validates both individual table structures and
/// their relationships according to ECMA-335 specifications.
///
/// # Thread Safety
///
/// This validator is [`Send`] and [`Sync`] as all validation operations are read-only
/// and operate on immutable metadata structures.
pub struct RawTableValidator;

impl RawTableValidator {
    /// Creates a new table structure validator.
    ///
    /// Initializes a validator instance that can be used to validate metadata
    /// table structures across multiple assemblies. The validator is stateless
    /// and can be reused safely across multiple validation operations.
    ///
    /// # Returns
    ///
    /// A new [`RawTableValidator`] instance ready for validation operations.
    ///
    /// # Thread Safety
    ///
    /// The returned validator is thread-safe and can be used concurrently.
    #[must_use]
    pub fn new() -> Self {
        Self
    }

    /// Validates that all required metadata tables are present in the assembly.
    ///
    /// According to ECMA-335, certain tables are mandatory for valid .NET assemblies.
    /// This method ensures that essential tables like Module and Assembly (for executables)
    /// are present and accessible with valid content.
    ///
    /// # Arguments
    ///
    /// * `assembly_view` - Assembly metadata view containing table data via [`crate::metadata::cilassemblyview::CilAssemblyView`]
    ///
    /// # Returns
    ///
    /// * `Ok(())` - All required tables are present with valid content
    /// * `Err(`[`crate::Error::ValidationRawFailed`]`)` - Required tables missing or invalid
    ///
    /// # Errors
    ///
    /// Returns [`crate::Error::ValidationRawFailed`] if:
    /// - Module table is missing (always required by ECMA-335)
    /// - Module table is present but contains zero rows (at least one required)
    /// - Assembly table contains more than one row (ECMA-335 limit violation)
    fn validate_required_tables(assembly_view: &CilAssemblyView) -> Result<()> {
        let tables = assembly_view
            .tables()
            .ok_or_else(|| malformed_error!("Assembly view does not contain metadata tables"))?;

        let module_table = tables.table::<ModuleRaw>().ok_or_else(|| {
            malformed_error!("Module table is required but not present in assembly")
        })?;
        if module_table.row_count == 0 {
            return Err(malformed_error!(
                "Module table is present but contains no rows - at least one Module row is required"
            ));
        }

        if let Some(assembly_table) = tables.table::<AssemblyRaw>() {
            if assembly_table.row_count > 1 {
                return Err(malformed_error!(
                    "Assembly table contains {} rows but can contain at most 1 row",
                    assembly_table.row_count
                ));
            }
        }

        Ok(())
    }

    /// Validates the structural integrity of individual metadata tables.
    ///
    /// Ensures that each present table has consistent structure, valid row counts,
    /// and proper internal organization. This includes checking that table data
    /// is properly aligned and that row counts match table headers. Uses
    /// [`crate::dispatch_table_type`] for comprehensive coverage.
    ///
    /// # Arguments
    ///
    /// * `assembly_view` - Assembly metadata view containing table data via [`crate::metadata::cilassemblyview::CilAssemblyView`]
    ///
    /// # Returns
    ///
    /// * `Ok(())` - All table structures are valid and consistent
    /// * `Err(`[`crate::Error::ValidationRawFailed`]`)` - Structure violations found
    ///
    /// # Errors
    ///
    /// Returns [`crate::Error::ValidationRawFailed`] if:
    /// - Table row counts exceed maximum allowed values (0x00FFFFFF)
    /// - RID values within table rows are inconsistent with expected sequential numbering
    /// - Internal table structure inconsistencies are detected during iteration
    fn validate_table_structures(assembly_view: &CilAssemblyView) -> Result<()> {
        let tables = assembly_view
            .tables()
            .ok_or_else(|| malformed_error!("Assembly view does not contain metadata tables"))?;

        for table_id in TableId::iter() {
            dispatch_table_type!(table_id, |RawType| {
                if let Some(table) = tables.table::<RawType>() {
                    let row_count = table.row_count;

                    if row_count > 0x00FF_FFFF {
                        return Err(malformed_error!(
                            "{:?} table contains {} rows, exceeding maximum of {} rows",
                            table_id,
                            row_count,
                            0x00FF_FFFF
                        ));
                    }
                }
            });
        }

        Ok(())
    }

    /// Validates cross-table dependencies and relationships.
    ///
    /// Ensures that metadata tables maintain proper relationships with each other
    /// according to ECMA-335 specifications. This includes validating that list-based
    /// references (TypeDef field lists, method lists) remain within table bounds.
    ///
    /// # Arguments
    ///
    /// * `assembly_view` - Assembly metadata view containing table data via [`crate::metadata::cilassemblyview::CilAssemblyView`]
    ///
    /// # Returns
    ///
    /// * `Ok(())` - All table dependencies are satisfied
    /// * `Err(`[`crate::Error::ValidationRawFailed`]`)` - Dependency violations found
    ///
    /// # Errors
    ///
    /// Returns [`crate::Error::ValidationRawFailed`] if:
    /// - TypeDef field list references exceed Field table row count
    /// - TypeDef method list references exceed MethodDef table row count
    /// - List-based cross-table references are out of bounds
    fn validate_table_dependencies(assembly_view: &CilAssemblyView) -> Result<()> {
        let tables = assembly_view
            .tables()
            .ok_or_else(|| malformed_error!("Assembly view does not contain metadata tables"))?;

        if let (Some(typedef_table), Some(field_table)) =
            (tables.table::<TypeDefRaw>(), tables.table::<FieldRaw>())
        {
            for typedef_row in typedef_table {
                if typedef_row.field_list != 0 && typedef_row.field_list > field_table.row_count + 1
                {
                    return Err(malformed_error!(
                        "TypeDef RID {} references field list starting at RID {} but Field table only has {} rows",
                        typedef_row.rid,
                        typedef_row.field_list,
                        field_table.row_count
                    ));
                }
            }
        }

        if let (Some(typedef_table), Some(method_table)) =
            (tables.table::<TypeDefRaw>(), tables.table::<MethodDefRaw>())
        {
            for typedef_row in typedef_table {
                if typedef_row.method_list != 0
                    && typedef_row.method_list > method_table.row_count + 1
                {
                    return Err(malformed_error!(
                        "TypeDef RID {} references method list starting at RID {} but MethodDef table only has {} rows",
                        typedef_row.rid,
                        typedef_row.method_list,
                        method_table.row_count
                    ));
                }
            }
        }

        Ok(())
    }
}

impl RawValidator for RawTableValidator {
    /// Validates the structural integrity of all metadata tables in the assembly.
    ///
    /// Performs comprehensive validation of table structures, including:
    /// 1. Required table presence validation (Module, Assembly, etc.)
    /// 2. Table structure consistency (headers, row counts)
    /// 3. Cross-table dependency validation
    /// 4. Table ordering according to ECMA-335 requirements
    ///
    /// This method provides foundational guarantees about metadata table integrity
    /// that higher-level validators can rely upon during semantic validation.
    ///
    /// # Arguments
    ///
    /// * `context` - Raw validation context containing assembly view and configuration
    ///
    /// # Returns
    ///
    /// * `Ok(())` - All table structures are valid and meet ECMA-335 requirements
    /// * `Err(`[`crate::Error::ValidationRawFailed`]`)` - Table structure violations found
    ///
    /// # Errors
    ///
    /// Returns [`crate::Error::ValidationRawFailed`] for:
    /// - Missing required tables (Module, Assembly for executables)
    /// - Invalid table row counts or corrupted table headers
    /// - Cross-table dependency violations
    /// - Tables present in invalid order
    ///
    /// # Thread Safety
    ///
    /// This method is thread-safe and performs only read-only operations on metadata.
    fn validate_raw(&self, context: &RawValidationContext) -> Result<()> {
        let assembly_view = context.assembly_view();

        Self::validate_required_tables(assembly_view)?;
        Self::validate_table_structures(assembly_view)?;
        Self::validate_table_dependencies(assembly_view)?;

        Ok(())
    }

    fn name(&self) -> &'static str {
        "RawTableValidator"
    }

    fn priority(&self) -> u32 {
        190
    }

    fn should_run(&self, context: &RawValidationContext) -> bool {
        context.config().enable_structural_validation
    }
}

impl Default for RawTableValidator {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{
        metadata::validation::ValidationConfig,
        prelude::*,
        test::{
            factories::validation::raw_structure_table::*, get_testfile_wb, validator_test,
            TestAssembly,
        },
    };

    /// Comprehensive test for RawTableValidator using the centralized test harness.
    ///
    /// This test validates all core validation rules implemented by RawTableValidator:
    /// 1. Required Table Presence (validate_required_tables) - Tests Module table requirements
    /// 2. Table Structure Validation (validate_table_structures) - Tests row counts
    /// 3. Cross-Table Dependencies (validate_table_dependencies) - Tests field/method list bounds
    ///
    /// # Test Coverage
    ///
    /// - **Positive Test**: Clean WindowsBase.dll passes all validation rules
    /// - **Multiple Assembly Rows**: Assembly table with >1 rows triggers Malformed error
    /// - **Field List Violation**: TypeDef.field_list beyond Field table bounds triggers Malformed error
    /// - **Method List Violation**: TypeDef.method_list beyond MethodDef table bounds triggers Malformed error
    /// - **Empty Module Table**: Module table with 0 rows triggers Malformed error (FIXED - Delete operations now applied)
    ///
    /// # Future Test Coverage (TODO)
    ///
    ///
    /// # Test Configuration
    ///
    /// - enable_structural_validation: true (required for RawTableValidator)
    /// - Other validators disabled for isolation
    ///
    /// # Validation Rules Tested
    ///
    /// The test systematically validates ECMA-335 compliance for:
    /// - Module table presence and single row requirement
    /// - Assembly table maximum 1 row constraint
    /// - Table structure consistency and RID sequential numbering
    /// - Cross-table reference bounds checking
    ///
    /// Each test case targets exactly one validation rule to ensure test isolation
    /// and clear error attribution.
    #[test]
    fn test_raw_table_validator() -> Result<()> {
        let validator = RawTableValidator::new();
        let config = ValidationConfig {
            enable_structural_validation: true,
            ..Default::default()
        };

        validator_test(
            raw_table_validator_file_factory,
            "RawTableValidator",
            "Malformed",
            config,
            |context| validator.validate_raw(context),
        )
    }

    /// Test that RawTableValidator configuration flags work correctly.
    ///
    /// Verifies that the validator respects enable_structural_validation configuration setting.
    #[test]
    fn test_raw_table_validator_configuration() -> Result<()> {
        let validator = RawTableValidator::new();

        fn clean_only_factory() -> Result<Vec<TestAssembly>> {
            let Some(clean_testfile) = get_testfile_wb() else {
                return Err(Error::Other("WindowsBase.dll not available".to_string()));
            };
            Ok(vec![TestAssembly::new(&clean_testfile, true)])
        }

        // Test disabled configuration
        let result_disabled = validator_test(
            clean_only_factory,
            "RawTableValidator",
            "Malformed",
            ValidationConfig {
                enable_structural_validation: false,
                ..Default::default()
            },
            |context| {
                if validator.should_run(context) {
                    validator.validate_raw(context)
                } else {
                    Ok(())
                }
            },
        );

        assert!(
            result_disabled.is_ok(),
            "Configuration test failed: validator should not run when disabled"
        );

        // Test enabled configuration
        let result_enabled = validator_test(
            clean_only_factory,
            "RawTableValidator",
            "Malformed",
            ValidationConfig {
                enable_structural_validation: true,
                ..Default::default()
            },
            |context| validator.validate_raw(context),
        );

        assert!(
            result_enabled.is_ok(),
            "Configuration test failed: validator should run when enabled"
        );
        Ok(())
    }

    /// Test RawTableValidator priority and metadata.
    ///
    /// Verifies validator metadata is correct for proper execution ordering.
    #[test]
    fn test_raw_table_validator_metadata() {
        let validator = RawTableValidator::new();

        assert_eq!(validator.name(), "RawTableValidator");
        assert_eq!(validator.priority(), 190);

        let _config_enabled = ValidationConfig {
            enable_structural_validation: true,
            ..Default::default()
        };
        let _config_disabled = ValidationConfig {
            enable_structural_validation: false,
            ..Default::default()
        };
    }
}