From 662baeb13ca9f3e2a0e9397b806c039d08c0c414 Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Mon, 2 Feb 2026 20:50:57 +0000 Subject: [PATCH] Initial upload: Code Privacy Shield v0.1.0 --- src/code_privacy_shield/config.py | 217 ++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 src/code_privacy_shield/config.py diff --git a/src/code_privacy_shield/config.py b/src/code_privacy_shield/config.py new file mode 100644 index 0000000..e9c443a --- /dev/null +++ b/src/code_privacy_shield/config.py @@ -0,0 +1,217 @@ +import copy +import os +from pathlib import Path +from typing import Any, Dict, List, Optional + +import toml + + +class Config: + DEFAULT_CONFIG = { + "general": { + "preview_mode": False, + "quiet_mode": False, + "preserve_structure": True, + "recursive": True, + }, + "redaction": { + "default_replacement": "█" * 8, + "preserve_length": False, + "categories": { + "api_keys": True, + "pii": True, + "database": True, + "env_var": True, + "ip": True, + "authorization": True, + }, + }, + "custom_patterns": [], + "exclude_patterns": [ + "*.pyc", + "__pycache__", + ".git", + ".svn", + ".hg", + "node_modules", + ".env", + "*.egg-info", + "dist", + "build", + ], + "output": { + "format": "text", + "show_line_numbers": False, + "color_output": True, + }, + } + + def __init__(self, config_path: Optional[str] = None): + self.config_path = config_path + self.config: Dict[str, Any] = copy.deepcopy(self.DEFAULT_CONFIG) + + @classmethod + def get_default_config_path(cls) -> Path: + return Path.home() / ".config" / "cps" / "config.toml" + + @classmethod + def get_project_config_path(cls) -> Path: + return Path(".cps.toml") + + @classmethod + def get_local_config_path(cls) -> Path: + return Path(".cps") + + def load(self, path: Optional[str] = None) -> bool: + path = path or self.config_path + if path: + return self._load_from_file(path) + + loaded_any = False + project_path = self.get_project_config_path() + if project_path.exists(): + loaded_any = self._load_from_file(str(project_path)) or loaded_any + + default_path = self.get_default_config_path() + if default_path.exists(): + loaded_any = self._load_from_file(str(default_path)) or loaded_any + + return loaded_any + + def _load_from_file(self, path: str) -> bool: + try: + file_path = Path(path) + if not file_path.exists(): + return False + + content = file_path.read_text() + loaded_config = toml.loads(content) + + self._merge_config(self.config, loaded_config) + return True + except (toml.TomlDecodeError, IOError, PermissionError): + return False + + def _merge_config(self, base: Dict[str, Any], override: Dict[str, Any]) -> None: + for key, value in override.items(): + if key in base and isinstance(base[key], dict) and isinstance(value, dict): + self._merge_config(base[key], value) + else: + base[key] = value + + def get(self, key: str, default: Any = None) -> Any: + keys = key.split(".") + value = self.config + + for k in keys: + if isinstance(value, dict) and k in value: + value = value[k] + else: + return default + + return value + + def set(self, key: str, value: Any) -> None: + keys = key.split(".") + config = self.config + + for k in keys[:-1]: + if k not in config: + config[k] = {} + config = config[k] + + config[keys[-1]] = value + + def get_redaction_categories(self) -> Dict[str, bool]: + return self.config.get("redaction", {}).get("categories", {}) + + def get_exclude_patterns(self) -> List[str]: + return self.config.get("exclude_patterns", []) + + def get_custom_patterns(self) -> List[Dict[str, str]]: + return self.config.get("custom_patterns", []) + + def is_category_enabled(self, category: str) -> bool: + categories = self.get_redaction_categories() + return categories.get(category, True) + + def is_preview_mode(self) -> bool: + return self.config.get("general", {}).get("preview_mode", False) + + def is_quiet_mode(self) -> bool: + return self.config.get("general", {}).get("quiet_mode", False) + + def should_preserve_structure(self) -> bool: + return self.config.get("general", {}).get("preserve_structure", True) + + def should_recursive(self) -> bool: + return self.config.get("general", {}).get("recursive", True) + + def get_output_format(self) -> str: + return self.config.get("output", {}).get("format", "text") + + def save(self, path: Optional[str] = None) -> bool: + path = path or self.config_path + if not path: + return False + + try: + content = toml.dumps(self.config) + Path(path).write_text(content) + return True + except (IOError, PermissionError): + return False + + def reset_to_default(self) -> None: + self.config = copy.deepcopy(self.DEFAULT_CONFIG) + + @classmethod + def create_example_config(cls, path: str) -> bool: + example = { + "general": { + "preview_mode": False, + "quiet_mode": False, + "preserve_structure": True, + "recursive": True, + }, + "redaction": { + "default_replacement": "█" * 8, + "preserve_length": False, + "categories": { + "api_keys": True, + "pii": True, + "database": True, + "env_var": True, + "ip": True, + "authorization": True, + }, + }, + "custom_patterns": [ + { + "name": "Internal API", + "pattern": r"(?i)(internal[_-]?api[_-]?key['\"]?\s*[:=]\s*['\"]?)([a-zA-Z0-9_-]{16,})", + "category": "internal", + }, + ], + "exclude_patterns": [ + "*.pyc", + "__pycache__", + ".git", + ".svn", + "node_modules", + ".env", + "dist", + "build", + ], + "output": { + "format": "text", + "show_line_numbers": False, + "color_output": True, + }, + } + + try: + Path(path).write_text(toml.dumps(example)) + return True + except (IOError, PermissionError): + return False