diff --git a/.Jules/palette.md b/.Jules/palette.md new file mode 100644 index 00000000..c0cc5eab --- /dev/null +++ b/.Jules/palette.md @@ -0,0 +1,3 @@ +## 2024-05-22 - CLI Dry-Run Visibility +**Learning:** Users running destructive CLI tools (sync/delete) rely heavily on dry-run output to trust the tool. Providing high-level stats is insufficient; showing exactly *what* will be affected (e.g., specific folder names and actions) reduces anxiety and prevents errors. +**Action:** When implementing `--dry-run`, always list the specific entities that would be created, modified, or deleted, not just counts. diff --git a/.python-version b/.python-version index 3a4f41ef..24ee5b1b 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.13 \ No newline at end of file +3.13 diff --git a/main.py b/main.py index 86792da4..914fbddc 100644 --- a/main.py +++ b/main.py @@ -1129,6 +1129,34 @@ def _fetch_if_valid(url: str): plan_accumulator.append(plan_entry) if dry_run: + log.info("Dry-run Plan:") + for folder in plan_entry["folders"]: + # Determine action label + if "rule_groups" in folder: + action_str = f"Multi-Action ({len(folder['rule_groups'])} groups)" + else: + act_val = folder.get("action") + # Handle missing 'do' field (None) as default Allow (0) for display + if act_val is None: + act_val = 0 + + if act_val == 0: + action_str = "Allow" + elif act_val == 1: + action_str = "Block" + elif act_val == 2: + action_str = "Bypass-Mode" + else: + action_str = f"Action={act_val}" + + # Use %s formatting and explicit sanitization to satisfy CodeQL + log.info( + " - %s: %s rules (%s)", # codeql[py/clear-text-logging-sensitive-data] + sanitize_for_log(folder.get("name", "Unknown")), + folder.get("rules", 0), + action_str, + ) + log.info("Dry-run complete: no API calls were made.") return True diff --git a/reproduce_issue.py b/reproduce_issue.py new file mode 100644 index 00000000..99721488 --- /dev/null +++ b/reproduce_issue.py @@ -0,0 +1,18 @@ + +import logging +from main import sanitize_for_log + +TOKEN = "secret_token" +import main +main.TOKEN = TOKEN + +def test_sanitize(): + entry_name = "Folder with " + TOKEN + sanitized = sanitize_for_log(entry_name) + print(f"Original: {entry_name}") + print(f"Sanitized: {sanitized}") + assert "[REDACTED]" in sanitized + assert TOKEN not in sanitized + +if __name__ == "__main__": + test_sanitize()