Last active
August 7, 2026 15:46
-
-
Save wdormann/e93d2dc7eb9d35d513b9a7b30b43be75 to your computer and use it in GitHub Desktop.
Extract uBlock Origin rules from extension directory
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """ | |
| Extract uBlock Origin settings from Chrome's chrome.storage.local LevelDB. | |
| Outputs separate files for My filters, My rules, My switches, and whitelist. | |
| """ | |
| import plyvel | |
| import json | |
| import sys | |
| import os | |
| DB_PATH = sys.argv[1] if len(sys.argv) > 1 else os.path.expanduser(".") | |
| # Map storage keys -> output filenames | |
| OUTPUT_MAP = { | |
| "user-filters": "my-filters.txt", # Cosmetic & network filters | |
| "userFilters": "my-filters.txt", # Alternate key name | |
| "dynamicFilteringString": "my-rules.txt", # Dynamic filtering rules | |
| "hostnameSwitchesString": "my-switches.txt", # Hostname switches | |
| "whitelist": "trusted-sites.txt", # Trusted sites | |
| "urlFilteringString": "url-rules.txt", # URL filtering rules | |
| } | |
| def decode_value(raw_bytes): | |
| """Try JSON first, then UTF-8 fallback.""" | |
| try: | |
| return json.loads(raw_bytes) | |
| except (json.JSONDecodeError, UnicodeDecodeError): | |
| return raw_bytes.decode("utf-8", errors="ignore") | |
| def clean_text(data): | |
| """Turn JSON-escaped strings into clean text with real newlines.""" | |
| if isinstance(data, str): | |
| text = data.replace("\\n", "\n") | |
| text = text.replace("\\t", "\t") | |
| text = text.replace("\\r", "") | |
| text = text.replace("\\\\", "\\") | |
| text = text.replace("\x00", "").replace("\r", "") | |
| lines = [line.rstrip() for line in text.split("\n")] | |
| return "\n".join(lines) | |
| elif isinstance(data, list): | |
| return "\n".join(str(x) for x in data) | |
| elif isinstance(data, dict): | |
| return json.dumps(data, indent=2) | |
| else: | |
| return str(data) | |
| def main(): | |
| if not os.path.isdir(DB_PATH): | |
| print(f"Error: '{DB_PATH}' is not a directory.") | |
| sys.exit(1) | |
| db = plyvel.DB(DB_PATH, create_if_missing=False) | |
| found = set() | |
| for key, value in db: | |
| key_str = key.decode("utf-8", errors="ignore") | |
| data = decode_value(value) | |
| if key_str in OUTPUT_MAP: | |
| out_file = OUTPUT_MAP[key_str] | |
| cleaned = clean_text(data) | |
| with open(out_file, "w", encoding="utf-8") as f: | |
| f.write(cleaned) | |
| print(f" [+] {key_str:25s} -> {out_file} ({cleaned.count(chr(10))} lines)") | |
| found.add(key_str) | |
| else: | |
| preview = str(data)[:120].replace("\n", " ") | |
| print(f" [-] {key_str:25s} : {preview}...") | |
| db.close() | |
| print() | |
| if found: | |
| print(f"Extracted {len(found)} known key(s). Import locations:") | |
| if "user-filters" in found or "userFilters" in found: | |
| print(" - my-filters.txt -> Dashboard -> My filters") | |
| if "dynamicFilteringString" in found: | |
| print(" - my-rules.txt -> Dashboard -> My rules") | |
| if "hostnameSwitchesString" in found: | |
| print(" - my-switches.txt -> Dashboard -> My rules") | |
| if "whitelist" in found: | |
| print(" - trusted-sites.txt -> Dashboard -> Trusted sites") | |
| else: | |
| print("No known keys found. Check the [-] lines for actual key names.") | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment