Skip to main content
Glama

install_package_secure

Install Arch Linux packages with security checks that verify official repositories first, analyze AUR package trust scores, and block installations if critical security issues are detected.

Instructions

Install a package with comprehensive security checks. Workflow: 1. Check official repos first (safer) 2. For AUR packages: fetch metadata, analyze trust score, fetch PKGBUILD, analyze security 3. Block installation if critical security issues found 4. Check for AUR helper (paru > yay) 5. Install with --noconfirm if all checks pass. Only works on Arch Linux. Requires sudo access and paru/yay for AUR packages.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
package_nameYesName of package to install (checks official repos first, then AUR)

Implementation Reference

  • The core handler function implementing the install_package_secure MCP tool. It performs multi-step security validation (official repo check, metadata trust analysis, PKGBUILD safety scan) before installing packages via pacman or AUR helpers (paru/yay). Includes sudo checks and detailed status reporting.
    async def install_package_secure(package_name: str) -> Dict[str, Any]: """ Install a package with comprehensive security checks. Workflow: 1. Check if package exists in official repos first (safer) 2. For AUR packages: a. Fetch package metadata and analyze trust b. Fetch and analyze PKGBUILD for security issues c. Only proceed if security checks pass 3. Check for AUR helper availability (paru > yay) 4. Install with --noconfirm if all checks pass Args: package_name: Package name to install Returns: Dict with installation status and security analysis """ logger.info(f"Starting secure installation workflow for: {package_name}") # Only supported on Arch Linux if not IS_ARCH: return create_error_response( "NotSupported", "Package installation is only supported on Arch Linux systems", "This server is not running on Arch Linux" ) result = { "package": package_name, "installed": False, "security_checks": {}, "messages": [] } # ======================================================================== # STEP 0: Verify sudo is configured properly # ======================================================================== logger.info("[STEP 0/5] Verifying sudo configuration...") # Test if sudo password is cached or passwordless sudo is configured # Use skip_sudo_check=True to avoid recursive check test_exit_code, _, test_stderr = await run_command( ["sudo", "-n", "true"], timeout=5, check=False, skip_sudo_check=True ) if test_exit_code != 0: result["messages"].append("⚠️ SUDO PASSWORD REQUIRED") result["messages"].append("") result["messages"].append("Package installation requires sudo privileges.") result["messages"].append("Please choose one of these options:") result["messages"].append("") result["messages"].append("Option 1: Configure passwordless sudo for pacman:") result["messages"].append(" sudo visudo -f /etc/sudoers.d/arch-package-install") result["messages"].append(" Add: your_username ALL=(ALL) NOPASSWD: /usr/bin/pacman") result["messages"].append("") result["messages"].append("Option 2: Cache sudo password temporarily:") result["messages"].append(" Run: sudo -v") result["messages"].append(" Then retry the installation") result["messages"].append("") result["messages"].append("Option 3: Install manually in terminal:") result["messages"].append(f" sudo pacman -S {package_name}") result["security_checks"]["decision"] = "SUDO_REQUIRED" return result result["messages"].append("✅ Sudo privileges verified") # ======================================================================== # STEP 1: Check if package is in official repos first # ======================================================================== logger.info(f"[STEP 1/5] Checking if '{package_name}' is in official repos...") result["messages"].append("🔍 Checking official repositories first...") from .pacman import get_official_package_info official_pkg = await get_official_package_info(package_name) # If found in official repos, install directly with pacman if not official_pkg.get("error"): logger.info(f"Package '{package_name}' found in official repos - installing via pacman") result["messages"].append(f"✅ Package found in official repository: {official_pkg.get('repository', 'unknown')}") result["is_official"] = True result["security_checks"]["source"] = "official_repository" result["security_checks"]["risk_level"] = "LOW" result["security_checks"]["recommendation"] = "✅ SAFE - Official repository package" # Install using sudo pacman -S --noconfirm try: result["messages"].append("📦 Installing from official repository...") exit_code, stdout, stderr = await run_command( ["sudo", "pacman", "-S", "--noconfirm", package_name], timeout=300, # 5 minutes for installation check=False ) if exit_code == 0: result["installed"] = True result["messages"].append(f"✅ Successfully installed {package_name} from official repository") logger.info(f"Successfully installed official package: {package_name}") else: result["messages"].append(f"❌ Installation failed: {stderr}") logger.error(f"pacman installation failed: {stderr}") # Check for sudo password issues if "password" in stderr.lower() or "sudo" in stderr.lower(): result["messages"].append("") result["messages"].append("⚠️ SUDO PASSWORD REQUIRED") result["messages"].append("To enable passwordless installation, run one of these commands:") result["messages"].append("1. For passwordless sudo (less secure):") result["messages"].append(" sudo visudo -f /etc/sudoers.d/arch-package-install") result["messages"].append(" Add: your_username ALL=(ALL) NOPASSWD: /usr/bin/pacman") result["messages"].append("2. Or run the installation manually in your terminal:") result["messages"].append(f" sudo pacman -S {package_name}") result["install_output"] = stdout result["install_errors"] = stderr return result except Exception as e: logger.error(f"Installation failed: {e}") return create_error_response( "InstallError", f"Failed to install official package: {str(e)}" ) # ======================================================================== # STEP 2: Package is in AUR - fetch and analyze metadata # ======================================================================== logger.info(f"[STEP 2/5] Package not in official repos - checking AUR...") result["messages"].append("⚠️ Package not in official repos - checking AUR...") result["is_official"] = False # Search AUR for package aur_info = await get_aur_info(package_name) if aur_info.get("error"): return create_error_response( "NotFound", f"Package '{package_name}' not found in official repos or AUR" ) # Extract actual package data (may be wrapped in warning) pkg_data = aur_info.get("data", aur_info) result["messages"].append(f"📦 Found in AUR: {pkg_data.get('name')} v{pkg_data.get('version')}") # Analyze package metadata for trust logger.info(f"[STEP 3/5] Analyzing package metadata for trust indicators...") result["messages"].append("🔍 Analyzing package metadata (votes, maintainer, age)...") metadata_analysis = analyze_package_metadata_risk(pkg_data) result["security_checks"]["metadata_analysis"] = metadata_analysis result["messages"].append(f"📊 Trust Score: {metadata_analysis['trust_score']}/100") result["messages"].append(f" {metadata_analysis['recommendation']}") # ======================================================================== # STEP 3: Fetch and analyze PKGBUILD # ======================================================================== logger.info(f"[STEP 4/5] Fetching and analyzing PKGBUILD for security issues...") result["messages"].append("🔍 Fetching PKGBUILD for security analysis...") try: pkgbuild_content = await get_pkgbuild(package_name) result["messages"].append(f"✅ PKGBUILD fetched ({len(pkgbuild_content)} bytes)") # Analyze PKGBUILD for security issues result["messages"].append("🛡️ Analyzing PKGBUILD for security threats...") pkgbuild_analysis = analyze_pkgbuild_safety(pkgbuild_content) result["security_checks"]["pkgbuild_analysis"] = pkgbuild_analysis result["messages"].append(f"🛡️ Risk Score: {pkgbuild_analysis['risk_score']}/100") result["messages"].append(f" {pkgbuild_analysis['recommendation']}") # Log findings if pkgbuild_analysis["red_flags"]: result["messages"].append(f" 🚨 {len(pkgbuild_analysis['red_flags'])} CRITICAL issues found!") for flag in pkgbuild_analysis["red_flags"][:3]: # Show first 3 result["messages"].append(f" - Line {flag['line']}: {flag['issue']}") if pkgbuild_analysis["warnings"]: result["messages"].append(f" ⚠️ {len(pkgbuild_analysis['warnings'])} warnings found") # Check if package is safe to install if not pkgbuild_analysis["safe"]: result["messages"].append("❌ INSTALLATION BLOCKED - Security analysis failed") result["messages"].append(" Package has critical security issues and will NOT be installed") result["security_checks"]["decision"] = "BLOCKED" result["security_checks"]["reason"] = "Critical security issues detected in PKGBUILD" logger.warning(f"Installation blocked for {package_name} due to security issues") return result # Additional check for high-risk warnings if len(pkgbuild_analysis["warnings"]) >= 5: result["messages"].append("⚠️ HIGH RISK - Multiple suspicious patterns detected") result["messages"].append(" Manual review recommended before installation") result["security_checks"]["decision"] = "REVIEW_RECOMMENDED" except ValueError as e: logger.error(f"Failed to fetch PKGBUILD: {e}") return create_error_response( "FetchError", f"Failed to fetch PKGBUILD for security analysis: {str(e)}" ) # ======================================================================== # STEP 4: Check for AUR helper # ======================================================================== logger.info(f"[STEP 5/5] Checking for AUR helper (paru/yay)...") result["messages"].append("🔧 Checking for AUR helper...") aur_helper = get_aur_helper() if not aur_helper: result["messages"].append("❌ No AUR helper found (paru or yay)") result["messages"].append(" Please install an AUR helper:") result["messages"].append(" - Recommended: paru (pacman -S paru)") result["messages"].append(" - Alternative: yay") result["security_checks"]["decision"] = "NO_HELPER" return result result["messages"].append(f"✅ Using AUR helper: {aur_helper}") result["aur_helper"] = aur_helper # ======================================================================== # STEP 5: Install package with AUR helper # ======================================================================== result["messages"].append(f"📦 Installing {package_name} via {aur_helper} (no confirmation)...") logger.info(f"Installing AUR package {package_name} with {aur_helper}") try: # Install with --noconfirm flag exit_code, stdout, stderr = await run_command( [aur_helper, "-S", "--noconfirm", package_name], timeout=600, # 10 minutes for AUR package build check=False ) if exit_code == 0: result["installed"] = True result["messages"].append(f"✅ Successfully installed {package_name} from AUR") result["security_checks"]["decision"] = "INSTALLED" logger.info(f"Successfully installed AUR package: {package_name}") else: result["messages"].append(f"❌ Installation failed with exit code {exit_code}") result["messages"].append(f" Error: {stderr}") result["security_checks"]["decision"] = "INSTALL_FAILED" logger.error(f"AUR installation failed for {package_name}: {stderr}") # Check for sudo password issues if "password" in stderr.lower() or "sudo" in stderr.lower(): result["messages"].append("") result["messages"].append("⚠️ SUDO PASSWORD REQUIRED") result["messages"].append("To enable passwordless installation for AUR packages:") result["messages"].append("1. For passwordless sudo for pacman:") result["messages"].append(" sudo visudo -f /etc/sudoers.d/arch-aur-install") result["messages"].append(" Add: your_username ALL=(ALL) NOPASSWD: /usr/bin/pacman") result["messages"].append("2. Or run the installation manually in your terminal:") result["messages"].append(f" {aur_helper} -S {package_name}") result["install_output"] = stdout result["install_errors"] = stderr except Exception as e: logger.error(f"Installation failed: {e}") result["messages"].append(f"❌ Installation exception: {str(e)}") result["security_checks"]["decision"] = "INSTALL_ERROR" return result
  • Package-level import of the install_package_secure handler from aur.py, making it available as part of the arch_ops_server module for MCP tool registration.
    from .aur import ( search_aur, get_aur_info, get_pkgbuild, get_aur_file, analyze_pkgbuild_safety, analyze_package_metadata_risk, install_package_secure )
  • Tool metadata definition providing schema-like information: input (package_name: str), category, platform requirements, permissions, workflow context, related and prerequisite tools.
    "install_package_secure": ToolMetadata( name="install_package_secure", category="lifecycle", platform="arch", permission="write", workflow="installation", related_tools=[ "check_updates_dry_run", "verify_package_integrity", "get_transaction_history" ], prerequisite_tools=[ "get_official_package_info", "analyze_pkgbuild_safety", "analyze_package_metadata_risk" ] ),
  • Key helper function called by the handler to statically analyze PKGBUILD files for security risks using regex patterns for destructive commands, backdoors, obfuscation, etc., determining if installation should proceed.
    def analyze_pkgbuild_safety(pkgbuild_content: str) -> Dict[str, Any]: """ Perform comprehensive safety analysis on PKGBUILD content. Checks for: - Dangerous commands (rm -rf /, dd, fork bombs, etc.) - Obfuscated code (base64, eval, encoding tricks) - Network activity (reverse shells, data exfiltration) - Binary downloads and execution - Privilege escalation attempts - Cryptocurrency mining patterns - Source URL validation - Suspicious file operations Args: pkgbuild_content: Raw PKGBUILD text Returns: Dict with detailed safety analysis results including: - safe: boolean - red_flags: critical security issues - warnings: suspicious patterns - info: informational notices - risk_score: 0-100 (higher = more dangerous) - recommendation: action recommendation """ import re from urllib.parse import urlparse red_flags = [] # Critical security issues warnings = [] # Suspicious but not necessarily malicious info = [] # Informational notices lines = pkgbuild_content.split('\n') logger.debug(f"Analyzing PKGBUILD with {len(lines)} lines") # ======================================================================== # CRITICAL PATTERNS - Definitely malicious # ======================================================================== dangerous_patterns = [ # Destructive commands (r"rm\s+-rf\s+/[^a-zA-Z]", "CRITICAL: rm -rf / or /something detected - system destruction"), (r"\bdd\b.*if=/dev/(zero|random|urandom).*of=/dev/sd", "CRITICAL: dd overwriting disk detected"), (r":\(\)\{.*:\|:.*\}", "CRITICAL: Fork bomb detected"), (r"\bmkfs\.", "CRITICAL: Filesystem formatting detected"), (r"fdisk.*-w", "CRITICAL: Partition table modification detected"), # Reverse shells and backdoors (r"/dev/tcp/\d+\.\d+\.\d+\.\d+/\d+", "CRITICAL: Reverse shell via /dev/tcp detected"), (r"nc\s+-[^-]*e\s+/bin/(ba)?sh", "CRITICAL: Netcat reverse shell detected"), (r"bash\s+-i\s+>&\s+/dev/tcp/", "CRITICAL: Interactive reverse shell detected"), (r"python.*socket.*connect", "CRITICAL: Python socket connection (potential backdoor)"), (r"perl.*socket.*connect", "CRITICAL: Perl socket connection (potential backdoor)"), # Malicious downloads and execution (r"curl[^|]*\|\s*(ba)?sh", "CRITICAL: Piping curl to shell (remote code execution)"), (r"wget[^|]*\|\s*(ba)?sh", "CRITICAL: Piping wget to shell (remote code execution)"), (r"curl.*-o.*&&.*chmod\s+\+x.*&&\s*\./", "CRITICAL: Download, make executable, and run pattern"), # Crypto mining patterns (r"xmrig|minerd|cpuminer|ccminer", "CRITICAL: Cryptocurrency miner detected"), (r"stratum\+tcp://", "CRITICAL: Mining pool connection detected"), (r"--donate-level", "CRITICAL: XMRig miner option detected"), # Rootkit/malware installation (r"chattr\s+\+i", "CRITICAL: Making files immutable (rootkit technique)"), (r"/etc/ld\.so\.preload", "CRITICAL: LD_PRELOAD manipulation (rootkit technique)"), (r"HISTFILE=/dev/null", "CRITICAL: History clearing (covering tracks)"), ] # ======================================================================== # SUSPICIOUS PATTERNS - Require careful review # ======================================================================== suspicious_patterns = [ # Obfuscation techniques (r"base64\s+-d", "Obfuscation: base64 decoding detected"), (r"xxd\s+-r", "Obfuscation: hex decoding detected"), (r"\beval\b", "Obfuscation: eval usage (can execute arbitrary code)"), (r"\$\(.*base64.*\)", "Obfuscation: base64 in command substitution"), (r"openssl\s+enc\s+-d", "Obfuscation: encrypted content decoding"), (r"echo.*\|.*sh", "Obfuscation: piping echo to shell"), (r"printf.*\|.*sh", "Obfuscation: piping printf to shell"), # Suspicious permissions and ownership (r"chmod\s+[0-7]*7[0-7]*7", "Dangerous: world-writable permissions"), (r"chown\s+root", "Suspicious: changing ownership to root"), (r"chmod\s+[u+]*s", "Suspicious: setuid/setgid (privilege escalation risk)"), # Suspicious file operations (r"mktemp.*&&.*chmod", "Suspicious: temp file creation with permission change"), (r">/dev/null\s+2>&1", "Suspicious: suppressing all output (hiding activity)"), (r"nohup.*&", "Suspicious: background process that persists"), # Network activity (r"curl.*-s.*-o", "Network: silent download detected"), (r"wget.*-q.*-O", "Network: quiet download detected"), (r"nc\s+-l", "Network: netcat listening mode (potential backdoor)"), (r"socat", "Network: socat usage (advanced networking tool)"), (r"ssh.*-R\s+\d+:", "Network: SSH reverse tunnel detected"), # Data exfiltration (r"curl.*-X\s+POST.*--data", "Data exfiltration: HTTP POST with data"), (r"tar.*\|.*ssh", "Data exfiltration: tar over SSH"), (r"scp.*-r.*\*", "Data exfiltration: recursive SCP"), # Systemd/init manipulation (r"systemctl.*enable.*\.service", "System: enabling systemd service"), (r"/etc/systemd/system/", "System: systemd unit file modification"), (r"update-rc\.d", "System: SysV init modification"), (r"@reboot", "System: cron job at reboot"), # Kernel module manipulation (r"modprobe", "System: kernel module loading"), (r"insmod", "System: kernel module insertion"), (r"/lib/modules/", "System: kernel module directory access"), # Compiler/build chain manipulation (r"gcc.*-fPIC.*-shared", "Build: creating shared library (could be malicious)"), (r"LD_PRELOAD=", "Build: LD_PRELOAD manipulation (function hijacking)"), ] # ======================================================================== # INFORMATIONAL PATTERNS - Good to know but not necessarily bad # ======================================================================== info_patterns = [ (r"sudo\s+", "Info: sudo usage detected"), (r"git\s+clone", "Info: git clone detected"), (r"make\s+install", "Info: make install detected"), (r"pip\s+install", "Info: pip install detected"), (r"npm\s+install", "Info: npm install detected"), (r"cargo\s+install", "Info: cargo install detected"), ] # ======================================================================== # SCAN PATTERNS LINE BY LINE # ======================================================================== for i, line in enumerate(lines, 1): # Skip comments and empty lines for pattern matching stripped_line = line.strip() if stripped_line.startswith('#') or not stripped_line: continue # Check dangerous patterns (red flags) for pattern, message in dangerous_patterns: if re.search(pattern, line, re.IGNORECASE): logger.warning(f"Red flag found at line {i}: {message}") red_flags.append({ "line": i, "content": line.strip()[:100], # Limit length for output "issue": message, "severity": "CRITICAL" }) # Check suspicious patterns for pattern, message in suspicious_patterns: if re.search(pattern, line, re.IGNORECASE): logger.info(f"Warning found at line {i}: {message}") warnings.append({ "line": i, "content": line.strip()[:100], "issue": message, "severity": "WARNING" }) # Check informational patterns for pattern, message in info_patterns: if re.search(pattern, line, re.IGNORECASE): info.append({ "line": i, "content": line.strip()[:100], "issue": message, "severity": "INFO" }) # ======================================================================== # ANALYZE SOURCE URLs # ======================================================================== source_urls = re.findall(r'source=\([^)]+\)|source_\w+=\([^)]+\)', pkgbuild_content, re.MULTILINE) suspicious_domains = [] # Known suspicious TLDs and patterns suspicious_tlds = ['.tk', '.ml', '.ga', '.cf', '.gq', '.cn', '.ru'] suspicious_url_patterns = [ (r'bit\.ly|tinyurl|shorturl', "URL shortener (hides true destination)"), (r'pastebin|hastebin|paste\.ee', "Paste site (common for malware hosting)"), (r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', "Raw IP address (suspicious)"), ] for source_block in source_urls: # Extract URLs from source array urls = re.findall(r'https?://[^\s\'"]+', source_block) for url in urls: try: parsed = urlparse(url) domain = parsed.netloc.lower() # Check for suspicious TLDs if any(domain.endswith(tld) for tld in suspicious_tlds): warnings.append({ "line": 0, "content": url, "issue": f"Suspicious domain TLD: {domain}", "severity": "WARNING" }) suspicious_domains.append(domain) # Check for suspicious URL patterns for pattern, message in suspicious_url_patterns: if re.search(pattern, url, re.IGNORECASE): warnings.append({ "line": 0, "content": url, "issue": message, "severity": "WARNING" }) except Exception as e: logger.debug(f"Failed to parse URL {url}: {e}") # ======================================================================== # DETECT BINARY DOWNLOADS # ======================================================================== binary_extensions = ['.bin', '.exe', '.AppImage', '.deb', '.rpm', '.jar', '.apk'] for ext in binary_extensions: if ext in pkgbuild_content.lower(): warnings.append({ "line": 0, "content": "", "issue": f"Binary file type detected: {ext}", "severity": "WARNING" }) # ======================================================================== # CALCULATE RISK SCORE # ======================================================================== # Risk scoring: red_flags = 50 points each, warnings = 5 points each, cap at 100 risk_score = min(100, (len(red_flags) * 50) + (len(warnings) * 5)) # ======================================================================== # GENERATE RECOMMENDATION # ======================================================================== if len(red_flags) > 0: recommendation = "❌ DANGEROUS - Critical security issues detected. DO NOT INSTALL." safe = False elif len(warnings) >= 5: recommendation = "⚠️ HIGH RISK - Multiple suspicious patterns detected. Review carefully before installing." safe = False elif len(warnings) > 0: recommendation = "⚠️ CAUTION - Some suspicious patterns detected. Manual review recommended." safe = True # Technically safe but needs review else: recommendation = "✅ SAFE - No critical issues detected. Standard review still recommended." safe = True logger.info(f"PKGBUILD analysis complete: {len(red_flags)} red flags, {len(warnings)} warnings, risk score: {risk_score}") return { "safe": safe, "red_flags": red_flags, "warnings": warnings, "info": info, "risk_score": risk_score, "suspicious_domains": list(set(suspicious_domains)), "recommendation": recommendation, "summary": { "total_red_flags": len(red_flags), "total_warnings": len(warnings), "total_info": len(info), "lines_analyzed": len(lines) } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/nihalxkumar/arch-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server