T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/tracker.py:69
- Finding
- Repeated Refresh-Day Reset Causes Progress Data Loss<![CDATA[ ## Vulnerability Details **File Location**: `scripts/tracker.py`, lines 69–91 **Vulnerability Type**: Improper state validation resulting in repeated destructive resets **Risk Level**: Medium ### Vulnerable Code ```python for name, config in events.items(): if not config.get("enabled", True): continue key = config["key"] refresh_day = config["refresh_day"] # 判断今天是否是刷新日,且当前时间超过6点 if weekday == refresh_day and now.hour >= refresh_hour: # 重置进度 data["progress"][key] = {"done": 0, "total": config["total"]} data["last_reset"] = data.get("last_reset", {}) data["last_reset"][key] = now.strftime("%Y-%m-%d") save_progress(data) return data ``` ### Technical Analysis The reset routine records the date of the latest reset in `data["last_reset"][key]`, but it never checks that value before resetting progress. Therefore, on an event's configured refresh day, every invocation at or after 06:00 overwrites the event's progress with zero. The main program calls `check_reset()` for every command. Several command handlers, including status and update operations, also invoke the reset routine. Consequently, progress entered after the intended weekly reset can be silently deleted by a subsequent invocation on the same day. This violates the intended once-per-refresh-cycle behavior and creates a local data-integrity flaw. No external code execution, privilege escalation, or unauthorized system access is enabled by this issue. ### Attack Path 1. The system reaches an event's configured refresh day and the local time passes 06:00. 2. A user invokes the tracker, causing the expected weekly reset. 3. The user records new progress later that day with `tracker.py --update`. 4. A local user or automated process invokes any tracker command again. 5. `check_reset()` sees only that the weekday and hour match the reset condition. 6 ...[truncated 967 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Before resetting an event, compare its stored reset date with the current date and perform the reset only when they differ. ```python today_string = now.strftime("%Y-%m-%d") last_reset = data.setdefault("last_reset", {}) progress = data.setdefault("progress", {}) changed = False for name, config in events.items(): if not config.get("enabled", True): continue key = config["key"] refresh_day = config["refresh_day"] if ( weekday == refresh_day and now.hour >= refresh_hour and last_reset.get(key) != today_string ): progress[key] = { "done": 0, "total": config["total"], } last_reset[key] = today_string changed = True if changed: save_progress(data) ``` Additional hardening measures: 1. Save the progress file once after processing all events instead of once per event. 2. Write updates atomically by creating a securely permissioned temporary file in the destination directory and replacing the original file with `os.replace()`. 3. Validate that loaded JSON objects contain dictionaries for `progress` and `last_reset` before using them. 4. Catch specific exceptions such as `FileNotFoundError` and `json.JSONDecodeError` rather than suppressing every exception. 5. Add regression tests for: - Multiple calls after 06:00 on the same refresh day. - An update performed after the daily reset. - A status request following that update. - The next scheduled refresh cycle. - Multiple events sharing the same refresh day. ]]>
