Sistema NEXUS v3.4 migrado con: Estructura principal: - core/orchestration: Sistema SIMCO + CAPVED (27 directivas, 28 perfiles) - core/catalog: Catalogo de funcionalidades reutilizables - shared/knowledge-base: Base de conocimiento compartida - devtools/scripts: Herramientas de desarrollo - control-plane/registries: Control de servicios y CI/CD - orchestration/: Configuracion de orquestacion de agentes Proyectos incluidos (11): - gamilit (submodule -> GitHub) - trading-platform (OrbiquanTIA) - erp-suite con 5 verticales: - erp-core, construccion, vidrio-templado - mecanicas-diesel, retail, clinicas - betting-analytics - inmobiliaria-analytics - platform_marketing_content - pos-micro, erp-basico Configuracion: - .gitignore completo para Node.js/Python/Docker - gamilit como submodule (git@github.com:rckrdmrd/gamilit-workspace.git) - Sistema de puertos estandarizado (3005-3199) Generated with NEXUS v3.4 Migration System EPIC-010: Configuracion Git y Repositorios
91 lines
2.9 KiB
Python
91 lines
2.9 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
import logging
|
|
import re
|
|
|
|
|
|
class SensitiveDataFilter(logging.Filter):
|
|
|
|
def __init__(self, sensitive_keys):
|
|
super().__init__()
|
|
if sensitive_keys is None:
|
|
self._sensitive_keys = set()
|
|
else:
|
|
self._sensitive_keys = sensitive_keys
|
|
self._compile_patterns()
|
|
|
|
def _compile_patterns(self):
|
|
"""Precompile regex patterns for all sensitive keys, matching double/single-quoted JSON
|
|
entries.
|
|
|
|
:return: None
|
|
"""
|
|
self._patterns = []
|
|
for key in self._sensitive_keys:
|
|
# 1st group: "<key>" or '<key>'
|
|
# 2nd group: The quote char for the value
|
|
pattern = re.compile(rf'([\'"]{key}[\'"])\s*:\s*([\'"])([^\'"]+)\2')
|
|
self._patterns.append(pattern)
|
|
|
|
def filter(self, record):
|
|
"""Override of `logging` to mask any sensitive data in record.args before the record is
|
|
emitted. Always returns True to allow the record through.
|
|
|
|
:return: True
|
|
:rtype: bool
|
|
"""
|
|
if len(self._patterns) != len(self._sensitive_keys): # If keys changed.
|
|
self._compile_patterns() # Recompile the patterns.
|
|
|
|
record.args = self._mask(record.args)
|
|
return True
|
|
|
|
def _mask(self, data):
|
|
"""Recursively mask dicts, iterables, and strings.
|
|
|
|
:param data: The data to mask.
|
|
:return: The masked data.
|
|
"""
|
|
if isinstance(data, dict):
|
|
masked_dict = {}
|
|
for k, v in data.items():
|
|
masked_dict[k] = "[REDACTED]" if k in self._sensitive_keys else self._mask(v)
|
|
return masked_dict
|
|
if isinstance(data, (list, tuple, set)):
|
|
cls = type(data)
|
|
return cls(self._mask(v) for v in data)
|
|
if isinstance(data, str):
|
|
return self._mask_string(data)
|
|
return data
|
|
|
|
def _mask_string(self, text):
|
|
"""Apply each pattern, replacing the value with [REDACTED].
|
|
|
|
:param str text: The string to mask.
|
|
:return: The masked string.
|
|
:rtype: str
|
|
"""
|
|
def replace(m):
|
|
# 1st group: "<key>" or '<key>'
|
|
# 2nd group: The quote char for the value
|
|
quote = m.group(2)
|
|
return f"{m.group(1)}: {quote}[REDACTED]{quote}"
|
|
|
|
for pattern in self._patterns:
|
|
text = re.sub(pattern, replace, text)
|
|
return text
|
|
|
|
|
|
def get_payment_logger(name, sensitive_keys=None):
|
|
"""Return a logger with a SensitiveDataFilter added if sensitive keys are provided.
|
|
|
|
:param str name: The name of the logger.
|
|
:param set sensitive_keys: The keys of the payment data that should not be logged.
|
|
:return: The logger.
|
|
:rtype: logging.Logger
|
|
"""
|
|
logger = logging.getLogger(name)
|
|
if sensitive_keys is not None:
|
|
logger.addFilter(SensitiveDataFilter(sensitive_keys))
|
|
return logger
|