Skip to content

Instantly share code, notes, and snippets.

@wdormann
Last active August 7, 2026 15:46
Show Gist options
  • Select an option

  • Save wdormann/e93d2dc7eb9d35d513b9a7b30b43be75 to your computer and use it in GitHub Desktop.

Select an option

Save wdormann/e93d2dc7eb9d35d513b9a7b30b43be75 to your computer and use it in GitHub Desktop.
Extract uBlock Origin rules from extension directory
#!/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