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
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
//! Owned ownership validator for parent-child relationship validation in resolved metadata.
//!
//! This validator provides comprehensive validation of ownership relationships within the context
//! of fully resolved .NET metadata. It operates on resolved type structures to validate
//! parent-child ownership patterns, nested class relationships, inheritance hierarchies,
//! and access modifier consistency across type boundaries. This validator runs with priority 160
//! in the owned validation stage.
//!
//! # Architecture
//!
//! The ownership validation system implements comprehensive ownership relationship validation in sequential order:
//! 1. **Type-Member Ownership Validation** - Ensures resolved types properly own their members
//! 2. **Nested Class Ownership Validation** - Validates nested class ownership rules in type hierarchies
//! 3. **Inheritance Relationship Validation** - Validates inheritance relationships between resolved types
//! 4. **Access Modifier Consistency Validation** - Checks access modifier consistency with semantic ownership
//! 5. **Cross-Assembly Relationship Validation** - Validates ownership relationships across assembly boundaries
//!
//! The implementation validates ownership constraints according to ECMA-335 specifications,
//! ensuring proper type ownership patterns and access control consistency.
//! All validation includes ownership tree construction and relationship verification.
//!
//! # Key Components
//!
//! - [`crate::metadata::validation::validators::owned::relationships::ownership::OwnedOwnershipValidator`] - Main validator implementation providing comprehensive ownership validation
//!
//! # Usage Examples
//!
//! ```rust,no_run
//! use dotscope::metadata::validation::{OwnedOwnershipValidator, OwnedValidator, OwnedValidationContext};
//!
//! # fn get_context() -> OwnedValidationContext<'static> { unimplemented!() }
//! let context = get_context();
//! let validator = OwnedOwnershipValidator::new();
//!
//! // Check if validation should run based on configuration
//! if validator.should_run(&context) {
//!     validator.validate_owned(&context)?;
//! }
//! # Ok::<(), dotscope::Error>(())
//! ```
//!
//! # Error Handling
//!
//! This validator returns [`crate::Error::ValidationOwnedFailed`] for:
//! - Invalid type-member ownership relationships (orphaned members, incorrect ownership)
//! - Nested class ownership violations (invalid containment hierarchies, circular dependencies)
//! - Inheritance relationship inconsistencies (broken parent-child relationships, invalid accessibility)
//! - Access modifier inheritance violations (inconsistent accessibility across boundaries)
//! - Cross-assembly ownership relationship failures (broken external ownership patterns)
//!
//! # 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 resolved metadata structures.
//!
//! # Integration
//!
//! This validator integrates with:
//! - [`crate::metadata::validation::validators::owned::relationships`] - Part of the owned relationship validation stage
//! - [`crate::metadata::validation::engine::ValidationEngine`] - Orchestrates validator execution
//! - [`crate::metadata::validation::traits::OwnedValidator`] - Implements the owned validation interface
//! - [`crate::metadata::cilobject::CilObject`] - Source of resolved type structures
//! - [`crate::metadata::validation::context::OwnedValidationContext`] - Provides validation execution context
//! - [`crate::metadata::validation::config::ValidationConfig`] - Controls validation execution via enable_cross_table_validation flag
//!
//! # References
//!
//! - [ECMA-335 II.10](https://ecma-international.org/wp-content/uploads/ECMA-335_6th_edition_june_2012.pdf) - Type system and ownership rules
//! - [ECMA-335 II.22.32](https://ecma-international.org/wp-content/uploads/ECMA-335_6th_edition_june_2012.pdf) - NestedClass table and containment relationships
//! - [ECMA-335 II.22.37](https://ecma-international.org/wp-content/uploads/ECMA-335_6th_edition_june_2012.pdf) - TypeDef table and member ownership

use std::{collections::HashSet, sync::Arc};

use crate::{
    metadata::{
        tables::TypeAttributes,
        typesystem::CilType,
        validation::{
            context::{OwnedValidationContext, ValidationContext},
            traits::OwnedValidator,
        },
    },
    Error, Result,
};

/// Foundation validator for parent-child ownership relationships in resolved metadata structures.
///
/// Ensures the structural integrity and consistency of ownership relationships in resolved .NET metadata,
/// validating that types properly own their members, nested class relationships follow ownership rules,
/// inheritance hierarchies maintain proper ownership patterns, and access control consistency is preserved
/// across type boundaries. This validator operates on resolved type structures to provide essential
/// guarantees about ownership integrity and relationship consistency.
///
/// The validator implements comprehensive coverage of ownership validation according to
/// ECMA-335 specifications, ensuring proper type ownership patterns, inheritance
/// relationships, and cross-assembly relationship integrity in the resolved metadata object model.
///
/// # Thread Safety
///
/// This validator is [`Send`] and [`Sync`] as all validation operations are read-only
/// and operate on immutable resolved metadata structures.
pub struct OwnedOwnershipValidator;

impl OwnedOwnershipValidator {
    /// Creates a new ownership validator instance.
    ///
    /// Initializes a validator instance that can be used to validate ownership relationships
    /// across multiple assemblies. The validator is stateless and can be reused safely
    /// across multiple validation operations.
    ///
    /// # Returns
    ///
    /// A new [`crate::metadata::validation::validators::owned::relationships::ownership::OwnedOwnershipValidator`] 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 resolved types properly own their members.
    ///
    /// Ensures that type-member ownership relationships are consistent and that
    /// members are properly contained within their declaring types.
    ///
    /// # Arguments
    ///
    /// * `context` - Owned validation context containing resolved structures
    ///
    /// # Returns
    ///
    /// * `Ok(())` - All type-member ownership relationships are valid
    /// * `Err(`[`crate::Error::ValidationOwnedFailed`]`)` - Ownership violations found
    fn validate_type_member_ownership(&self, context: &OwnedValidationContext) -> Result<()> {
        let methods = context.object().methods();

        for type_entry in context.target_assembly_types() {
            if type_entry.get_external().is_some() {
                continue;
            }

            // Validate method ownership relationships
            for (_idx, method_ref) in type_entry.methods.iter() {
                if let Some(method_token) = method_ref.token() {
                    if let Some(method) = methods.get(&method_token) {
                        let method_value = method.value();

                        // Validate method name consistency with ownership
                        if method_value.name.is_empty() {
                            return Err(Error::ValidationOwnedFailed {
                                validator: self.name().to_string(),
                                message: format!(
                                    "Type '{}' owns method with empty name (token 0x{:08X})",
                                    type_entry.name,
                                    method_token.value()
                                ),
                            });
                        }

                        // Validate method accessibility is compatible with owning type
                        let method_access_flags = method_value.flags_access.bits();
                        self.validate_method_accessibility(
                            &type_entry.name,
                            type_entry.flags,
                            &method_value.name,
                            method_access_flags,
                        )?;

                        // Validate special method ownership rules
                        if method_value.name.starts_with('.') {
                            let method_modifier_flags = method_value.flags_modifiers.bits();
                            self.validate_special_method_ownership(
                                &type_entry.name,
                                &method_value.name,
                                method_modifier_flags,
                            )?;
                        }
                    } else {
                        return Err(Error::ValidationOwnedFailed {
                            validator: self.name().to_string(),
                            message: format!(
                                "Type '{}' claims ownership of non-existent method token 0x{:08X}",
                                type_entry.name,
                                method_token.value()
                            ),
                        });
                    }
                } else {
                    return Err(Error::ValidationOwnedFailed {
                        validator: self.name().to_string(),
                        message: format!(
                            "Type '{}' has method reference without valid token",
                            type_entry.name
                        ),
                    });
                }
            }

            // Validate field ownership relationships
            for (_, field) in type_entry.fields.iter() {
                if field.name.is_empty() {
                    return Err(Error::ValidationOwnedFailed {
                        validator: self.name().to_string(),
                        message: format!("Type '{}' owns field with empty name", type_entry.name),
                    });
                }

                // Validate field accessibility is compatible with owning type
                self.validate_field_accessibility_ownership(
                    &type_entry.name,
                    type_entry.flags,
                    &field.name,
                    field.flags,
                )?;
            }

            // Validate property ownership relationships
            for (_, property) in type_entry.properties.iter() {
                if property.name.is_empty() {
                    return Err(Error::ValidationOwnedFailed {
                        validator: self.name().to_string(),
                        message: format!(
                            "Type '{}' owns property with empty name",
                            type_entry.name
                        ),
                    });
                }
            }

            // Validate event ownership relationships
            for (_, event) in type_entry.events.iter() {
                if event.name.is_empty() {
                    return Err(Error::ValidationOwnedFailed {
                        validator: self.name().to_string(),
                        message: format!("Type '{}' owns event with empty name", type_entry.name),
                    });
                }
            }
        }

        Ok(())
    }

    /// Validates method accessibility ownership consistency.
    fn validate_method_accessibility(
        &self,
        type_name: &str,
        type_flags: u32,
        method_name: &str,
        method_flags: u32,
    ) -> Result<()> {
        let type_visibility = type_flags & TypeAttributes::VISIBILITY_MASK;
        let method_visibility = method_flags & 0x0007; // MethodAttributes visibility mask

        // Methods in non-public types cannot have effective public visibility
        if type_visibility != TypeAttributes::PUBLIC && method_visibility == 6
        /* Public */
        {
            // This is actually valid - public methods in internal types are allowed
            // Their effective accessibility is limited by the type's accessibility
        }

        // Validate that method visibility is within valid range
        if method_visibility > 6 {
            return Err(Error::ValidationOwnedFailed {
                validator: self.name().to_string(),
                message: format!(
                    "Method '{method_name}' in type '{type_name}' has invalid visibility value: 0x{method_visibility:02X}"
                ),

            });
        }

        Ok(())
    }

    /// Validates special method ownership rules.
    fn validate_special_method_ownership(
        &self,
        type_name: &str,
        method_name: &str,
        method_flags: u32,
    ) -> Result<()> {
        match method_name {
            ".ctor" => {
                // Instance constructors should not be static
                if method_flags & 0x0010 != 0 {
                    // Static flag
                    return Err(Error::ValidationOwnedFailed {
                        validator: self.name().to_string(),
                        message: format!(
                            "Instance constructor '.ctor' in type '{type_name}' cannot be static"
                        ),
                    });
                }
            }
            ".cctor" => {
                // Static constructors must be static
                if method_flags & 0x0010 == 0 {
                    // Static flag is NOT set - this is an error
                    return Err(Error::ValidationOwnedFailed {
                        validator: self.name().to_string(),
                        message: format!(
                            "Static constructor '.cctor' in type '{type_name}' must be static"
                        ),
                    });
                }
                // If static flag is set, this is correct - no error
            }
            _ => {
                // Other special methods (finalizers, etc.) follow normal rules
            }
        }

        Ok(())
    }

    /// Validates field accessibility ownership consistency.
    fn validate_field_accessibility_ownership(
        &self,
        type_name: &str,
        _type_flags: u32,
        field_name: &str,
        field_flags: u32,
    ) -> Result<()> {
        let field_visibility = field_flags & 0x0007; // FieldAttributes visibility mask

        // Validate that field visibility is within valid range
        if field_visibility > 6 {
            return Err(Error::ValidationOwnedFailed {
                validator: self.name().to_string(),
                message: format!(
                    "Field '{field_name}' in type '{type_name}' has invalid visibility value: 0x{field_visibility:02X}"
                ),

            });
        }

        Ok(())
    }

    /// Validates nested class ownership rules in type hierarchies.
    ///
    /// Ensures that nested class relationships follow proper ownership rules,
    /// containment hierarchies are correctly formed.
    ///
    /// # Arguments
    ///
    /// * `context` - Owned validation context containing resolved structures
    ///
    /// # Returns
    ///
    /// * `Ok(())` - All nested class ownership rules are satisfied
    /// * `Err(`[`crate::Error::ValidationOwnedFailed`]`)` - Ownership violations found
    fn validate_nested_class_ownership_rules(
        &self,
        context: &OwnedValidationContext,
    ) -> Result<()> {
        let target_types = context.target_assembly_types();

        let target_type_pointers: HashSet<*const CilType> = target_types
            .iter()
            .map(|t| std::ptr::from_ref::<CilType>(t.as_ref()))
            .collect();

        for type_entry in target_types {
            // Validate nested type ownership consistency
            // Only validate nested types that belong to the current assembly
            for (_, nested_ref) in type_entry.nested_types.iter() {
                if let Some(nested_type) = nested_ref.upgrade() {
                    // Check if the nested type belongs to the current assembly
                    // by checking if it's in the target assembly types collection
                    let nested_type_ptr = nested_type.as_ref() as *const CilType;
                    let is_target_assembly_type = target_type_pointers.contains(&nested_type_ptr);

                    if is_target_assembly_type {
                        // Validate nested type accessibility constraints
                        self.validate_nested_type_accessibility_ownership(
                            &type_entry.name,
                            type_entry.flags,
                            &nested_type.name,
                            nested_type.flags,
                        )?;

                        // Note: Nested type naming validation is disabled as it's too strict for real-world .NET assemblies
                        // Most legitimate nested types have simple names like "DebuggingModes"
                    }
                    // Skip validation for nested types from other assemblies
                }
                // Skip broken references - they may be to external assemblies that aren't loaded
            }
        }

        Ok(())
    }

    /// Comprehensive circular dependency detection using DFS.
    fn validate_nested_type_circularity_deep(
        &self,
        current_type: &Arc<CilType>,
        recursion_stack: &mut HashSet<*const CilType>,
        depth: usize,
    ) -> Result<()> {
        const MAX_RECURSION_DEPTH: usize = 100;

        if depth > MAX_RECURSION_DEPTH {
            return Err(Error::ValidationOwnedFailed {
                validator: self.name().to_string(),
                message: format!(
                    "Maximum recursion depth ({}) exceeded for nested type validation starting with type '{}' (token 0x{:08X})",
                    MAX_RECURSION_DEPTH,
                    current_type.name,
                    current_type.token.value()
                ),

            });
        }

        let type_ptr = current_type.as_ref() as *const CilType;

        // Check for circular dependency - if this type is already in the current path
        if recursion_stack.contains(&type_ptr) {
            return Err(Error::ValidationOwnedFailed {
                validator: self.name().to_string(),
                message: format!(
                    "Circular nested type dependency detected involving type '{}' with token 0x{:08X} at depth {}",
                    current_type.name,
                    current_type.token.value(),
                    depth
                ),

            });
        }

        recursion_stack.insert(type_ptr);

        // Recursively check all nested types
        for (_, nested_ref) in current_type.nested_types.iter() {
            if let Some(nested_type) = nested_ref.upgrade() {
                self.validate_nested_type_circularity_deep(
                    &nested_type,
                    recursion_stack,
                    depth + 1,
                )?;
            }
        }

        recursion_stack.remove(&type_ptr);
        Ok(())
    }

    /// Validates nested type accessibility ownership constraints.
    fn validate_nested_type_accessibility_ownership(
        &self,
        container_name: &str,
        container_flags: u32,
        nested_name: &str,
        nested_flags: u32,
    ) -> Result<()> {
        let _ = container_flags & TypeAttributes::VISIBILITY_MASK;
        let nested_visibility = nested_flags & TypeAttributes::VISIBILITY_MASK;

        // Nested types must use nested visibility flags
        if !matches!(
            nested_visibility,
            TypeAttributes::NESTED_PUBLIC
                | TypeAttributes::NESTED_PRIVATE
                | TypeAttributes::NESTED_FAMILY
                | TypeAttributes::NESTED_ASSEMBLY
                | TypeAttributes::NESTED_FAM_AND_ASSEM
                | TypeAttributes::NESTED_FAM_OR_ASSEM
        ) {
            // Allow NotPublic (0) for some legitimate cases
            if nested_visibility != 0 && nested_visibility <= 7 {
                return Err(Error::ValidationOwnedFailed {
                    validator: self.name().to_string(),
                    message: format!(
                        "Nested type '{nested_name}' in container '{container_name}' uses top-level visibility instead of nested visibility: 0x{nested_visibility:02X}"
                    ),

                });
            }
        }

        // Note: Nested public types in non-public containers are allowed in .NET
        // Their effective accessibility is limited by the container's accessibility
        // This is a common and legitimate pattern in .NET assemblies
        // For example: internal class NativeMethods { public enum ColorSpace { ... } }
        // The enum is effectively internal despite being declared public

        Ok(())
    }
}

impl OwnedValidator for OwnedOwnershipValidator {
    fn validate_owned(&self, context: &OwnedValidationContext) -> Result<()> {
        self.validate_type_member_ownership(context)?;
        self.validate_nested_class_ownership_rules(context)?;

        // Note: Inheritance and cross-assembly validation are not implemented
        // as they require complex accessibility rules and assembly loading capabilities
        // that are beyond the current scope. The implemented validations provide
        // comprehensive ownership validation within the current assembly.

        Ok(())
    }

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

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

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

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

#[cfg(test)]
#[cfg_attr(feature = "skip-expensive-tests", allow(unused_imports))]
mod tests {
    use super::*;
    use crate::{
        metadata::validation::ValidationConfig,
        test::{
            factories::validation::ownership::owned_ownership_validator_file_factory,
            owned_validator_test,
        },
    };

    #[test]
    #[cfg(not(feature = "skip-expensive-tests"))]
    fn test_owned_ownership_validator() -> Result<()> {
        let validator = OwnedOwnershipValidator::new();
        let config = ValidationConfig {
            enable_cross_table_validation: true,
            ..Default::default()
        };

        owned_validator_test(
            owned_ownership_validator_file_factory,
            "OwnedOwnershipValidator",
            "ValidationOwnedFailed",
            config,
            |context| validator.validate_owned(context),
        )
    }
}