T09 · Insecure Skill Coding Practices
- Location
scripts/collect.sh:108- Finding
Date Filtering Occurs After Sensitive Session Metadata and Tool Content Are Collected
- Content
View full analysis
Vulnerability Details
File Location:
scripts/collect.sh:41-42, 108-145
Vulnerability Type: Insufficient data minimization and incomplete date filtering
Risk Level: MediumVulnerable Code
python target_date = sys.argv[1] files = sys.argv[2:]python # Count by role session_data["message_count"] += 1 if role == "user": session_data["user_message_count"] += 1 elif role == "assistant": session_data["assistant_message_count"] += 1 elif role == "toolResult": session_data["tool_result_count"] += 1 # Record tool call info tool_info = { "timestamp": timestamp, "content_preview": text[:200] if text else "", "success": "error" not in text.lower()[:500] if text else True } session_data["tool_calls"].append(tool_info) # Store message message_obj = { "role": role, "text": text, "timestamp": timestamp, "cost": cost } session_data["messages"].append(message_obj) ... # Filter messages to only those on target date session_data["messages"] = [ m for m in session_data["messages"] if m.get("timestamp", "")[:10] == target_date ]Technical Analysis
The collector reads and processes every message in every discovered session file before applying the requested date filter. Although the
messagesarray is filtered at the end, the following fields are populated from the complete transcript and are not subsequently filtered or recalculated:tool_calls, including up to 200 characters of tool-result contentmessage_countand role-specific counterstotal_costsession_startandsession_end- Calculated session duration
Consequently, a dataset described as representing one date can retain conversation-derived content and metadata from other dates. The redaction stage may reduce some recognizable PII, but it does not eliminate the underlying unnecessary collection ...[truncated 1595 chars]
- Remediation
View remediation
Remediation Suggestions
Apply the target-date check immediately after parsing each entry and before extracting message content or updating any aggregate:
python timestamp = entry.get("timestamp", "") if not timestamp or timestamp[:10] != target_date: continueThen calculate
messages,tool_calls, counters, cost, start/end timestamps, and duration exclusively from accepted entries. Additional hardening should include:- Recalculate all aggregates after filtering rather than preserving whole-session values.
- Filter
tool_callsby date even if early filtering is introduced, providing defense in depth. - Avoid retaining tool-result previews unless they are strictly required for analysis.
- Add tests using a multi-day JSONL fixture and assert that no content, costs, timestamps, or counts from other dates appear in output.
- Define whether date matching uses UTC or local time and parse timestamps rather than relying solely on a ten-character prefix.
