This commit is contained in:
217
src/code_privacy_shield/config.py
Normal file
217
src/code_privacy_shield/config.py
Normal file
@@ -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
|
||||||
Reference in New Issue
Block a user