Backup automatico script del 2026-09-27 07:00
This commit is contained in:
1 parent
f347badf3b
commit
09dcbbdadb
24 files changed
+1164
-193
No files matched your search
@@ -29,6 +29,33 @@ SKIP_PATH_PARTS = (
|
||||
)
|
||||
PRIORITY_PREFIXES = ("docs/", "ha/", "doc/", "README")
|
||||
|
||||
# File che esistono per contenere segreti: mai indicizzati (le collezioni RAG possono
|
||||
# essere condivise con la famiglia). .env.example resta indicizzabile.
|
||||
SECRET_FILE_RE = re.compile(
|
||||
r"(^|/)(\.env(\.(?!example$)[^/]*)?|id_(rsa|dsa|ecdsa|ed25519)(\.pub)?|\.htpasswd|\.netrc|\.pgpass"
|
||||
r"|\.claude/settings\.local\.json"
|
||||
r"|[^/]*\.(pem|key|p12|pfx|jks|keystore))$",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
_SECRET_KEY = r"[A-Za-z0-9_.-]*(?:pass(?:word|wd)?|secret|token|api[_-]?key|private[_-]?key)[A-Za-z0-9_.-]*"
|
||||
# chiave = valore / chiave: valore (valori letterali, non espressioni di codice)
|
||||
_SECRET_ASSIGN_RE = re.compile(
|
||||
rf"(?i)\b({_SECRET_KEY})(\s*[:=]\s*)([\"']?)([^\s\"'#(){{}}\[\]$<>,;]{{4,}})\3(?=[\s\"'#;,]|$)"
|
||||
)
|
||||
# stile config "chiave valore" a inizio riga (es. keepalived: auth_pass xxxx)
|
||||
_SECRET_CONF_RE = re.compile(rf"(?im)^(\s*{_SECRET_KEY})([ \t]+)(\S{{4,}})[ \t]*$")
|
||||
# password passate a comandi: sshpass -p X, --password X / --password=X
|
||||
_SECRET_CLI_RE = re.compile(r"(sshpass\s+-p\s*|--password[=\s]\s*)(\\?[\"']?)([^\s\"'\\]{3,})")
|
||||
_PRIVATE_KEY_RE = re.compile(r"-----BEGIN [A-Z ]*PRIVATE KEY-----.*?-----END [A-Z ]*PRIVATE KEY-----", re.S)
|
||||
|
||||
|
||||
def redact_secrets(text: str) -> str:
|
||||
"""Maschera i valori segreti prima di embedding e payload Qdrant."""
|
||||
text = _PRIVATE_KEY_RE.sub("<chiave privata rimossa>", text)
|
||||
text = _SECRET_CLI_RE.sub(lambda m: f"{m.group(1)}{m.group(2)}<redacted>", text)
|
||||
text = _SECRET_ASSIGN_RE.sub(lambda m: f"{m.group(1)}{m.group(2)}{m.group(3)}<redacted>{m.group(3)}", text)
|
||||
return _SECRET_CONF_RE.sub(lambda m: f"{m.group(1)}{m.group(2)}<redacted>", text)
|
||||
|
||||
|
||||
def _enabled() -> bool:
|
||||
flag = os.environ.get("GITEA_INDEX_ENABLED", "yes").strip().lower()
|
||||
@@ -71,7 +98,7 @@ def _repos_for_user(username: str) -> list[str]:
|
||||
|
||||
def _should_index_path(path: str) -> bool:
|
||||
lowered = path.lower()
|
||||
if any(part in lowered for part in SKIP_PATH_PARTS):
|
||||
if any(part in lowered for part in SKIP_PATH_PARTS) or SECRET_FILE_RE.search(path):
|
||||
return False
|
||||
ext = os.path.splitext(path)[1].lower()
|
||||
return bool(ext and ext in gitea.TEXT_EXTENSIONS)
|
||||
@@ -158,7 +185,7 @@ def index_file(
|
||||
if row and row["sha"] == sha and not force:
|
||||
return {"repo": repo, "path": path, "skipped": True, "sha": sha}
|
||||
|
||||
text = (file_data.get("content") or "").strip()
|
||||
text = redact_secrets((file_data.get("content") or "").strip())
|
||||
if not text:
|
||||
return {"repo": repo, "path": path, "chunks": 0, "sha": sha}
|
||||
|
||||
@@ -327,7 +354,7 @@ def gitea_collections_for_user(username: str, is_admin: bool = False) -> list[st
|
||||
|
||||
|
||||
def search_gitea_knowledge(username: str, query: str, limit: int = 8, is_admin: bool = False) -> list:
|
||||
vectors = embeddings.embed_texts([query])
|
||||
vectors = [embeddings.embed_query(query)]
|
||||
collections = gitea_collections_for_user(username, is_admin=is_admin)
|
||||
hits = qdrant_store.search(collections, vectors[0], limit=limit)
|
||||
for hit in hits:
|
||||
|
||||