from __future__ import annotations import logging logger = logging.getLogger(__name__) POLICY_OPEN = "open" POLICY_PAIRING = "pairing" POLICY_ALLOWLIST = "allowlist" POLICY_DISABLED = "disabled" VALID_POLICIES = {POLICY_OPEN, POLICY_PAIRING, POLICY_ALLOWLIST, POLICY_DISABLED} class GitLabSecurity: @staticmethod def resolve_dm_policy(account: dict) -> str: policy = account.get("dm_policy", POLICY_OPEN) if policy not in VALID_POLICIES: logger.warning("gitlab invalid dm_policy '%s', defaulting to open", policy) return POLICY_OPEN return policy @staticmethod def is_allowed(account: dict, username: str) -> tuple[bool, str | None]: policy = account.get("dm_policy", POLICY_OPEN) if policy == POLICY_DISABLED: return False, "gitlab-disabled" if policy == POLICY_OPEN: return True, None if policy == POLICY_ALLOWLIST: allow_from: list = account.get("allow_from", []) if username in allow_from or "*" in allow_from: return True, None return False, f"gitlab-not-in-allowlist:{username}" if policy == POLICY_PAIRING: allow_from: list = account.get("allow_from", []) if username in allow_from: return True, "gitlab-paired" return True, "gitlab-pairing-required" return False, "gitlab-unknown-policy" @staticmethod def collect_warnings(config: dict, account_id: str | None = None, account: dict | None = None) -> list[str]: warnings = [] if not account: return warnings if account.get("dm_policy") == "open" and not account.get("allow_from"): warnings.append("gitlab dmPolicy is 'open' without allowFrom — anyone can interact with the bot") if not account.get("ssl_verify"): warnings.append("gitlab ssl verification disabled — only use in trusted internal networks") return warnings @staticmethod def is_bot_own_message(payload: dict, bot_username: str) -> bool: user = payload.get("user", {}) return user.get("username", "") == bot_username