From 46f951f04bb629659130f95dd5235e3641b0b733 Mon Sep 17 00:00:00 2001 From: elhoim Date: Sun, 30 Aug 2026 23:42:36 +0000 Subject: [PATCH] Prune expired cache entries on save get_cached_response correctly ignores expired entries but neither it nor set_cached_response ever removed them, so cache.json (which stores full module responses, potentially secret-bearing) grew without bound. The only remedy was --purge-cache, which wipes the whole file. Add prune_expired_entries() and call it before persisting the cache in the query command, dropping any entry whose cached_at predates the configured TTL. This bounds cache.json to roughly one TTL window of entries instead of accumulating indefinitely. --- bin/cli.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/bin/cli.py b/bin/cli.py index 92267b1..c3163a8 100644 --- a/bin/cli.py +++ b/bin/cli.py @@ -669,6 +669,21 @@ def set_cached_response(cache: Dict[str, Any], key: str, response: Dict[str, Any } +def prune_expired_entries(cache: Dict[str, Any], now: int, ttl_seconds: int) -> int: + entries = cache.get("entries", {}) + if not isinstance(entries, dict): + return 0 + expired_keys = [ + key for key, entry in entries.items() + if not isinstance(entry, dict) + or not isinstance(entry.get("cached_at"), int) + or now - entry.get("cached_at", 0) > ttl_seconds + ] + for key in expired_keys: + del entries[key] + return len(expired_keys) + + def configure_module( modules: List[Dict[str, Any]], config_path: str, @@ -1087,6 +1102,11 @@ def main() -> int: f.write(markdown_report) log(f"Wrote markdown report to {args.markdown_output}") + pruned = prune_expired_entries(cache, now=int(time.time()), ttl_seconds=args.cache_ttl_seconds) + if pruned: + log(f"Pruned {pruned} expired cache entr{'y' if pruned == 1 else 'ies'}.") + cache_dirty = True + if cache_dirty: try: save_cache(args.cache_file, cache)