-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfixed
More file actions
242 lines (195 loc) · 10.7 KB
/
fixed
File metadata and controls
242 lines (195 loc) · 10.7 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
ZOOM CAPTURE - SECURITY FIXES REPORT
=====================================
Date: 2026-02-15
SUMMARY
-------
12 security issues were identified and fixed across all severity levels.
Two new dependencies were added (cryptography, Flask-Limiter) and two new
HTML templates were created (login.html, setup.html).
FILES MODIFIED
--------------
app.py - Auth, rate limiting, security headers, input validation
config_manager.py - Encrypted credential storage, auth data management
scheduler.py - Log sanitization and rotation
zoom_api.py - Download URL scheme validation
requirements.txt - Added cryptography, Flask-Limiter
services/zoom-archival.service - Replaced placeholder secrets with instructions
services/zoom-archival.plist - Replaced placeholder secrets with instructions
templates/base.html - Added logout button
FILES CREATED
-------------
templates/login.html - Password login page
templates/setup.html - First-run password setup page
=============================================================================
FIX #1 [CRITICAL] Encrypted Credential Storage
=============================================================================
WHAT CHANGED:
Zoom OAuth client_secret values are now encrypted at rest in config.json
using Fernet symmetric encryption (AES-128-CBC with HMAC authentication).
Encrypted values are stored with an "enc:" prefix. When the encryption
key environment variable (ZOOM_CAPTURE_ENCRYPTION_KEY) is set, existing
plaintext secrets are automatically migrated to encrypted form on startup.
USER EXPERIENCE IMPACT:
- You must set the ZOOM_CAPTURE_ENCRYPTION_KEY environment variable to
enable encryption. Without it, the app still works but credentials
remain in plaintext (with a startup warning).
- Generate a key once:
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
- Existing accounts are migrated automatically on first startup with
the key set. No manual re-entry of credentials needed.
- If you lose the encryption key, you will need to re-enter your Zoom
credentials for each account.
NEW DEPENDENCY: cryptography>=42.0,<44.0
=============================================================================
FIX #2 [CRITICAL] Flask Secret Key Validation
=============================================================================
WHAT CHANGED:
The app now detects and rejects the placeholder value
"CHANGE_ME_TO_RANDOM_SECRET". If FLASK_SECRET_KEY is unset or still the
placeholder, a random key is generated at startup with a warning log.
Service files now include generation instructions instead of a working
placeholder.
USER EXPERIENCE IMPACT:
- If you don't set FLASK_SECRET_KEY, the app still starts, but sessions
(including login) will not survive app restarts. A warning is logged.
- For production, set the env var to a stable random value:
python -c "import secrets; print(secrets.token_hex(32))"
=============================================================================
FIX #3 [HIGH] Dashboard Authentication
=============================================================================
WHAT CHANGED:
All dashboard routes now require authentication. On first launch, you are
redirected to a setup page to create a password (minimum 8 characters).
After setup, a login page is shown on every visit until you authenticate.
Passwords are stored as salted SHA-256 hashes in config.json. A logout
button was added to the navigation bar. Login attempts are rate-limited
to prevent brute-force attacks (see Fix #4).
USER EXPERIENCE IMPACT:
- On first launch after this update, you will be prompted to set a
dashboard password. This is a one-time setup step.
- Every subsequent visit requires logging in with your password.
- A "Logout" button is now visible in the top navigation bar.
- If you forget your password, delete the "auth" key from config.json
and restart the app to trigger the setup flow again.
=============================================================================
FIX #4 [HIGH] Rate Limiting
=============================================================================
WHAT CHANGED:
All API endpoints are now rate-limited (200 requests/hour, 50/minute by
default per IP). The login endpoint has a stricter limit of 10 per minute
to prevent brute-force password guessing. The manual-run endpoint is
limited to 5 per minute to prevent Zoom API quota abuse.
USER EXPERIENCE IMPACT:
- Normal usage will never hit these limits.
- If you trigger a rate limit, you'll see a 429 "Too Many Requests"
error. Wait a moment and retry.
- Automated scripts hitting the API excessively will be throttled.
NEW DEPENDENCY: Flask-Limiter>=3.5,<4.0
=============================================================================
FIX #5 [HIGH] Folder Browser Path Restrictions
=============================================================================
WHAT CHANGED:
The folder browser endpoint now resolves all paths to their canonical
form (preventing path traversal via ../ sequences). The test-paths
endpoint blocks writes to dangerous system directories (/, C:\Windows,
/etc, /usr, etc.). Returned paths are always fully resolved.
USER EXPERIENCE IMPACT:
- The folder browser dialog works exactly as before.
- You cannot set the archive or log path to a system root directory.
- This should not affect normal usage since archive paths are typically
user-created directories.
=============================================================================
FIX #6 [MEDIUM] Security Headers
=============================================================================
WHAT CHANGED:
Every HTTP response now includes:
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin
Content-Security-Policy: (restricts scripts/styles to self + CDN)
When served over HTTPS, Strict-Transport-Security (HSTS) is also set.
USER EXPERIENCE IMPACT:
- The dashboard cannot be embedded in iframes on other sites.
- Browsers will enforce stricter content loading rules.
- No visible change during normal use.
=============================================================================
FIX #7 [MEDIUM] Sanitized Error Messages
=============================================================================
WHAT CHANGED:
API error responses no longer include internal error details, stack
traces, or exception messages. Detailed errors are logged server-side
only. Users see generic messages like "Authentication failed. Check your
credentials." instead of raw exception text.
USER EXPERIENCE IMPACT:
- Error toast notifications will show simpler, friendlier messages.
- For debugging, check the server console/logs for detailed errors.
- This is more secure but slightly less informative in the browser.
=============================================================================
FIX #8 [MEDIUM] CSRF Token Expiration and Rotation
=============================================================================
WHAT CHANGED:
CSRF tokens now include a creation timestamp. Tokens older than 1 hour
are automatically rotated after the current request completes. This
limits the window during which a leaked token could be exploited.
USER EXPERIENCE IMPACT:
- If you leave the dashboard open for over an hour without interaction,
the next action may use a rotated token (the page will still work
because the new token is injected into the session).
- No visible change during normal use.
=============================================================================
FIX #9 [MEDIUM] Log Sanitization and Rotation
=============================================================================
WHAT CHANGED:
Meeting topics in logs are now sanitized: email addresses, phone numbers,
and SSN-like patterns are redacted (replaced with [EMAIL], [PHONE],
[REDACTED]). Topics are truncated to 80 characters. Log files now use
RotatingFileHandler with a 10 MB maximum size and 10 backup files,
preventing unbounded disk usage.
USER EXPERIENCE IMPACT:
- Log files will no longer grow indefinitely. Old entries rotate out.
- Meeting topics in logs may show [EMAIL] or [PHONE] instead of the
actual values. The actual meeting topic in Zoom is unchanged.
- The log viewer in the dashboard shows the same content as before,
just with potentially redacted meeting topics.
=============================================================================
FIX #10 [LOW] Download URL HTTPS Validation
=============================================================================
WHAT CHANGED:
The download_recording method in zoom_api.py now rejects any download
URL that does not start with "https://". This prevents a compromised
or tampered API response from redirecting downloads over unencrypted HTTP.
USER EXPERIENCE IMPACT:
- None during normal operation (Zoom always returns HTTPS URLs).
- If a non-HTTPS URL is somehow encountered, the download fails with
a clear error rather than silently proceeding insecurely.
=============================================================================
FIX #11 [LOW] Strict Run Time Validation
=============================================================================
WHAT CHANGED:
The run_time setting is now validated using Python's datetime.strptime
with the %H:%M format, then normalized to HH:MM. Previously, only
basic string splitting was used. A regex pre-check also ensures only
digits and a colon are accepted. The log date parameters in the log
API endpoints are now validated to be exactly 8 digits (YYYYMMDD) to
prevent path traversal.
USER EXPERIENCE IMPACT:
- Invalid time values that previously might have been accepted are now
rejected with a clear error message.
- No change for valid time inputs (e.g., "10:00", "9:30").
=============================================================================
SETUP STEPS AFTER UPDATE
=============================================================================
1. Install new dependencies:
pip install -r requirements.txt
2. Generate and set environment variables:
# Encryption key (for credential encryption at rest):
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
# Set as: ZOOM_CAPTURE_ENCRYPTION_KEY=<output>
# Flask secret key (for session persistence across restarts):
python -c "import secrets; print(secrets.token_hex(32))"
# Set as: FLASK_SECRET_KEY=<output>
3. Start the application. On first visit you will be prompted to set a
dashboard password.
4. Existing Zoom credentials in config.json will be automatically
encrypted on startup if ZOOM_CAPTURE_ENCRYPTION_KEY is set.