fix(observability): fall back instead of 500 on a malformed ?timezone= - #4182
Open
ntdat812 wants to merge 1 commit into
Open
fix(observability): fall back instead of 500 on a malformed ?timezone=#4182ntdat812 wants to merge 1 commit into
ntdat812 wants to merge 1 commit into
Conversation
ZoneInfo() rejects a bad key two different ways: an unknown but well-formed name raises ZoneInfoNotFoundError, while a malformed one raises ValueError before any lookup happens. Only the first was caught, so a malformed value escaped as an unhandled exception. The three Console endpoints take ?timezone= as a free-form query parameter with no pattern, and there is no generic exception handler on the app, so the request ends as a 500: ?timezone=Asia/Shanghai 200 ?timezone=Not/AZone 200 (unknown -> documented fallback) ?timezone=.. 500 ?timezone=/UTC 500 ?timezone=Asia/ 500 resolve_usage_timezone() additionally catches TypeError: that value comes from the config file, where a bare 'timezone: 8' parses as an int and would abort startup rather than fall back as its docstring promises.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
The three Console BFF endpoints (
/dashboard/summary,/tokens,/context-commits) accept?timezone=as a free-form query parameter with nopattern=. A malformed value returns HTTP 500 instead of falling back to the server default.ZoneInfo()rejects a bad key in two different ways, and only one of them was handled:ZoneInfo()raisesAsia/ShanghaiNot/AZoneZoneInfoNotFoundError..ValueError../../etc/passwdValueError/UTCValueErrorAsia/ValueError.ValueErrorThe malformed forms fail validation before any lookup —
ZoneInfoNotFoundErroris aKeyErrorsubclass, soexcept ZoneInfoNotFoundErrornever sees them.openviking/server/app.pyregisters handlers forOpenVikingError,RequestValidationErrorandStarletteHTTPExceptionbut no genericExceptionhandler, so theValueErrorreaches Starlette'sServerErrorMiddleware.Status codes above are measured, driving the real router over
httpx.ASGITransport(..., raise_app_exceptions=False).This is a robustness bug, not a path-traversal one —
ZoneInfois what rejects../../etc/passwd, and it rejects it correctly. The defect is that the rejection is not caught.Human Involvement
Related Issue
None — found while reading
usage_auditfor #4173.Type of Change
Changes Made
resolve_user_timezone()now catchesValueErroralongsideZoneInfoNotFoundError, matching what its docstring already promises for "an unknown or empty value".resolve_usage_timezone()catches the same pair plusTypeError. That value comes from the config file, where a baretimezone: 8parses as anint;ZoneInfo(8)raisesTypeErrorand would abort startup instead of falling back with a warning. Kept separate from the request path, where FastAPI already guaranteesstr | None._INVALID_TIMEZONEwith a comment on why two exception types are needed, so the pair does not drift back apart.Testing
New
tests/observability/test_usage_audit_timezone.py— 19 cases: the five malformed keys against both resolvers, the non-strconfig value, and one end-to-end pass per malformed key through the realUsageAuditQueryServicemounted on the console router (a fake service would not exercise the resolver at all).Reverting only
openviking/observability/usage_audit/time.py:With the change: 19 passed. The 3 that pass either way are the must-not-regress guards — a valid name still resolves, an unknown name still falls back, an empty value still falls back.
Whole directory:
pytest tests/observability/→ 49 passed.ruff checkandruff format --checkclean on both files.Checklist