-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathtest_security.py
More file actions
250 lines (192 loc) · 8.46 KB
/
test_security.py
File metadata and controls
250 lines (192 loc) · 8.46 KB
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
"""
Security vulnerability regression tests.
Covers:
- XSS prevention in HTML rendering
- SSRF prevention in URL validation
- Command injection prevention
All console output must be in English only (no emoji, no Chinese).
"""
import html
import sys
import os
import pytest
# ============================================================
# XSS Prevention Tests
# ============================================================
class TestXSSPrevention:
"""Verify that rendered HTML does not contain injected scripts."""
def test_markdown_renderer_strips_script_tags(self):
"""Script tags in markdown input must be removed."""
from video_transcript_api.utils.rendering.markdown_renderer import (
render_markdown_to_html,
)
result = render_markdown_to_html("<script>alert('xss')</script> hello")
assert "<script>" not in result
assert "alert(" not in result
def test_markdown_renderer_strips_onerror(self):
"""Event handler attributes must be removed."""
from video_transcript_api.utils.rendering.markdown_renderer import (
render_markdown_to_html,
)
result = render_markdown_to_html('<img src=x onerror="alert(1)">')
assert "onerror" not in result
def test_markdown_renderer_strips_iframe(self):
"""Iframe tags must be removed."""
from video_transcript_api.utils.rendering.markdown_renderer import (
render_markdown_to_html,
)
result = render_markdown_to_html('<iframe src="http://evil.com"></iframe>')
assert "<iframe" not in result
def test_markdown_renderer_blocks_javascript_urls(self):
"""javascript: URL scheme must be blocked in links."""
from video_transcript_api.utils.rendering.markdown_renderer import (
render_markdown_to_html,
)
result = render_markdown_to_html("[click](javascript:alert(1))")
assert "javascript:" not in result
def test_markdown_renderer_preserves_safe_html(self):
"""Normal markdown content must render correctly."""
from video_transcript_api.utils.rendering.markdown_renderer import (
render_markdown_to_html,
)
result = render_markdown_to_html("## Title\n\nhello **world**")
assert "<h2" in result
assert "<strong>" in result
def test_markdown_renderer_error_path_escapes(self):
"""The error fallback path must escape HTML."""
from video_transcript_api.utils.rendering.markdown_renderer import (
render_markdown_to_html,
)
# Force an error by passing something that causes markdown to fail
# but falls through to the escape path
# The error path: return f"<pre>{html_stdlib.escape(markdown_text)}</pre>"
# We can test directly via the escape function
import html as html_stdlib
malicious = "<script>alert(1)</script>"
escaped = html_stdlib.escape(malicious)
assert "<script>" not in escaped
assert "<script>" in escaped
def test_dialog_renderer_escapes_speaker_name(self):
"""Speaker names must be HTML-escaped in dialog rendering."""
from video_transcript_api.utils.rendering.dialog_renderer import (
DialogRenderer,
)
renderer = DialogRenderer()
# Create dialog data with malicious speaker name
text = '<script>alert(1)</script>: Hello world\nBob: Hi there'
# Even if not detected as dialog, test the escape logic
result = renderer.render_dialog_html(text)
assert "<script>" not in result
def test_dialog_renderer_escapes_content(self):
"""Dialog content must be HTML-escaped (no raw HTML tags in output)."""
from video_transcript_api.utils.rendering.dialog_renderer import (
DialogRenderer,
)
renderer = DialogRenderer()
text = 'Alice: <img src=x onerror=alert(1)>\nBob: Hello'
result = renderer.render_dialog_html(text)
# The raw <img tag must be escaped — it should appear as <img not <img
assert "<img src=x" not in result
def test_normal_text_renderer_escapes_html(self):
"""Normal text rendering must escape HTML tags (no raw tags in output)."""
from video_transcript_api.utils.rendering.dialog_renderer import (
DialogRenderer,
)
renderer = DialogRenderer()
malicious = "<script>alert(1)</script><img src=x onerror=alert(1)>"
result = renderer._render_normal_text(malicious)
# Raw tags must be escaped to <...> entities
assert "<script>" not in result
assert "<img src=x" not in result
# Verify the escaped versions are present
assert "<script>" in result
# ============================================================
# SSRF Prevention Tests
# ============================================================
class TestSSRFPrevention:
"""Verify that URL validation blocks dangerous URLs."""
def test_allows_normal_https_url(self):
from video_transcript_api.utils.url_validator import validate_url_safe
result = validate_url_safe("https://www.youtube.com/watch?v=abc123")
assert result == "https://www.youtube.com/watch?v=abc123"
def test_allows_normal_http_url(self):
from video_transcript_api.utils.url_validator import validate_url_safe
result = validate_url_safe("http://example.com/video.mp4")
assert result == "http://example.com/video.mp4"
def test_blocks_file_protocol(self):
from video_transcript_api.utils.url_validator import (
validate_url_safe,
URLValidationError,
)
with pytest.raises(URLValidationError):
validate_url_safe("file:///etc/passwd")
def test_blocks_ftp_protocol(self):
from video_transcript_api.utils.url_validator import (
validate_url_safe,
URLValidationError,
)
with pytest.raises(URLValidationError):
validate_url_safe("ftp://internal-server/data")
def test_blocks_localhost(self):
from video_transcript_api.utils.url_validator import (
validate_url_safe,
URLValidationError,
)
with pytest.raises(URLValidationError):
validate_url_safe("http://localhost:8080/secret")
def test_blocks_loopback_ip(self):
from video_transcript_api.utils.url_validator import (
validate_url_safe,
URLValidationError,
)
with pytest.raises(URLValidationError):
validate_url_safe("http://127.0.0.1:6006/")
def test_blocks_private_ip_10(self):
from video_transcript_api.utils.url_validator import (
validate_url_safe,
URLValidationError,
)
with pytest.raises(URLValidationError):
validate_url_safe("http://10.0.0.1/internal")
def test_blocks_private_ip_172(self):
from video_transcript_api.utils.url_validator import (
validate_url_safe,
URLValidationError,
)
with pytest.raises(URLValidationError):
validate_url_safe("http://172.16.0.1/admin")
def test_blocks_private_ip_192(self):
from video_transcript_api.utils.url_validator import (
validate_url_safe,
URLValidationError,
)
with pytest.raises(URLValidationError):
validate_url_safe("http://192.168.1.1/data")
def test_blocks_cloud_metadata(self):
from video_transcript_api.utils.url_validator import (
validate_url_safe,
URLValidationError,
)
with pytest.raises(URLValidationError):
validate_url_safe("http://169.254.169.254/latest/meta-data/")
def test_blocks_unspecified_address(self):
from video_transcript_api.utils.url_validator import (
validate_url_safe,
URLValidationError,
)
with pytest.raises(URLValidationError):
validate_url_safe("http://0.0.0.0/")
def test_blocks_empty_url(self):
from video_transcript_api.utils.url_validator import (
validate_url_safe,
URLValidationError,
)
with pytest.raises(URLValidationError):
validate_url_safe("")
def test_blocks_none_url(self):
from video_transcript_api.utils.url_validator import (
validate_url_safe,
URLValidationError,
)
with pytest.raises(URLValidationError):
validate_url_safe(None)