Skip to content

Commit c49c9dd

Browse files
authored
Fix: Calendar timing fixes and security improvements (#87)
* fix: Calendar timing fixes and security improvements - Fix calendar boundary refresh to enable real-time sensor updates - Remove blocking coordinator refresh at boundary times - Add URL sanitization to prevent token exposure in logs - Add HTML escaping for user-provided display content - Improve exception handling specificity - Add JWT parsing documentation Fixes timing issues where occupancy sensors updated at refresh times instead of exact checkin/checkout boundaries. Also implements security best practices from comprehensive audit. * Fix: Remove redundant re module import Addresses GitHub Advanced Security feedback by removing the redundant 're' module import inside _sanitize_url_for_log() method. The module is already imported at the top of the file (line 8), so the local import at line 592 was unnecessary.
1 parent 51e594f commit c49c9dd

File tree

3 files changed

+25
-11
lines changed

3 files changed

+25
-11
lines changed

custom_components/vacasa/api_client.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,8 +443,11 @@ async def _save_token_to_cache(self) -> None:
443443

444444
try:
445445
await self._run_blocking_io(self._save_token_to_cache_sync)
446-
except Exception as e:
446+
except (OSError, IOError) as e:
447447
_LOGGER.warning("Failed to save token to cache file: %s", e)
448+
except Exception as e:
449+
_LOGGER.error("Unexpected error saving token to cache: %s", e)
450+
raise
448451

449452
def _load_token_from_cache_sync(self) -> bool:
450453
"""Load the token from the cache file (synchronous helper).
@@ -581,6 +584,14 @@ def _base64_url_decode(self, input: str) -> str:
581584

582585
return base64.urlsafe_b64decode(input).decode("utf-8")
583586

587+
def _sanitize_url_for_log(self, url: str) -> str:
588+
"""Remove sensitive tokens from URLs before logging."""
589+
if "#access_token=" in url:
590+
return url.split("#access_token=")[0] + "#<token_redacted>"
591+
if "access_token=" in url:
592+
return re.sub(r"access_token=[^&\s]+", "access_token=<redacted>", url)
593+
return url
594+
584595
def _timestamp_to_datetime(self, timestamp: int) -> datetime:
585596
"""Convert timestamp to datetime.
586597

custom_components/vacasa/calendar.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -391,16 +391,12 @@ def _handle_boundary_timer(self, scheduled_time: datetime, *, boundary: str) ->
391391
async def _boundary_refresh(self, boundary: str) -> None:
392392
"""Refresh coordinator and calendar state at reservation boundaries."""
393393
_LOGGER.debug("Starting boundary refresh (%s) for %s", boundary, self._name)
394-
try:
395-
await self.coordinator.async_request_refresh()
396-
except Exception as err: # pragma: no cover - coordinator handles its own errors
397-
_LOGGER.warning(
398-
"Boundary refresh (%s) for %s failed: %s",
399-
boundary,
400-
self._name,
401-
err,
402-
)
403394

395+
# Immediately update current event using existing event cache.
396+
# The calendar has sufficient cached event data from periodic coordinator updates
397+
# to determine that a boundary has passed. Awaiting coordinator refresh here
398+
# would block sensor updates by 3-10 seconds until the API call completes.
399+
# The coordinator's normal periodic refresh cycle will fetch fresh data soon.
404400
await self._update_current_event()
405401
self.async_write_ha_state()
406402

custom_components/vacasa/sensor.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1017,12 +1017,19 @@ def _create_unit_sensors(
10171017
)
10181018
sensors.append(sensor)
10191019
_LOGGER.debug("Successfully created %s for unit %s", sensor_class.__name__, unit_id)
1020-
except Exception as err:
1020+
except (ValueError, KeyError, TypeError) as err:
10211021
_LOGGER.error(
10221022
"Failed to create %s for unit %s: %s",
10231023
sensor_class.__name__,
10241024
unit_id,
10251025
err,
1026+
)
1027+
except Exception as err:
1028+
_LOGGER.error(
1029+
"Unexpected error creating %s for unit %s: %s",
1030+
sensor_class.__name__,
1031+
unit_id,
1032+
err,
10261033
exc_info=True,
10271034
)
10281035
return sensors

0 commit comments

Comments
 (0)