HOME


Mini Shell 1.0
DIR: /proc/thread-self/root/opt/imunify360/venv/share/imunify360/scripts/
Upload File :
Current File : //proc/thread-self/root/opt/imunify360/venv/share/imunify360/scripts/remove_hardened_php.py
#!/opt/imunify360/venv/bin/python3
# removes HardenedPHP on CentOS + cPanel systems

import os
import subprocess
from contextlib import suppress
from tempfile import NamedTemporaryFile
from typing import Dict, List, Set, Tuple

REVERT_CPANEL_PHP_CMD = " --install "  # noqa: E501
EA_PHP_HARDENED_REPO = "imunify360-ea-php-hardened"


def repoquery(
    disablerepo: List[str] = None, enablerepo: List[str] = None
) -> Set[str]:
    """Returns a set of package names that are available in HardenedPHP repo"""
    repoquery_cmd = ["repoquery", "-a", "--qf=%{name}"]
    if disablerepo is not None:
        for repo in disablerepo:
            repoquery_cmd.append("--disablerepo=" + repo)
    if enablerepo is not None:
        for repo in enablerepo:
            repoquery_cmd.append("--enablerepo=" + repo)
    cp = subprocess.run(repoquery_cmd, stdout=subprocess.PIPE, check=True)
    return set(cp.stdout.decode().strip().split())


def list_installed() -> List[Tuple[str, str]]:
    """List all installed packages.

    Returns a list of packages. Each package is represented as a tuple
    (name, release)."""
    cp = subprocess.run(
        ["rpm", "-qa", "--queryformat", "%{NAME} %{RELEASE} "],
        stdout=subprocess.PIPE,
        check=True,
    )
    items = cp.stdout.decode().strip().split()
    return [(items[i], items[i + 1]) for i in range(0, len(items), 2)]


def prepare_yum_shell_file(actions: Dict[str, Set[str]]) -> str:
    """Creates temporary file for `yum shell` and returns its name."""
    ops = []
    ops.append("repo disable " + EA_PHP_HARDENED_REPO)
    for action, packages in actions.items():
        if len(packages):
            ops.append(action + " " + " ".join(packages))
    ops.append("run")
    with NamedTemporaryFile(delete=False) as cmd_list:
        content = "\n".join(ops).encode()
        cmd_list.write(content)
        cmd_list.close()
        return cmd_list.name


def prepare_yum_shell_actions(
    hardened_php_names: Set[str],
    available_names: Set[str],
    installed_names_releases: List[Tuple[str, str]],
) -> Dict[str, Set[str]]:
    """Creates a dictionary of actions to take to remove HardenedPHP.

    Returns a dict with key being an action to take (remove/downgrade) and
    value is a set of package names that need this action."""
    actions = {}
    cl_installed_names = set(
        [
            item[0]
            for item in installed_names_releases
            if "cloudlinux" in item[1]
        ]
    )
    not_available = cl_installed_names - available_names
    actions["remove"] = hardened_php_names & not_available
    still_available = hardened_php_names & available_names
    actions["downgrade"] = cl_installed_names & still_available
    return actions


def get_installed_hardened_php_names() -> Set[str]:
    """Returns a set of package names that are installed from HardenedPHP repo."""

    cp = subprocess.run(
        ["rpm", "-qa", "--queryformat", "%{NAME} %{RELEASE}\\n", "ea-*"],
        stdout=subprocess.PIPE,
        check=True,
    )
    items = set()
    for line in cp.stdout.decode().strip().split("\n"):
        if "cloudlinux" in line:
            items.add(line.split()[0])
    return items


def main():
    print("Starting", flush=True)
    # remove packages from HardenedPHP that are not available otherwise
    # downgrade remaining HardenedPHP packages
    hardened_php_names = get_installed_hardened_php_names()
    available_names = repoquery(disablerepo=[EA_PHP_HARDENED_REPO])
    installed_names_releases = list_installed()
    actions = prepare_yum_shell_actions(
        hardened_php_names, available_names, installed_names_releases
    )
    name = prepare_yum_shell_file(actions)
    for action, packages in actions.items():
        print("To {}: {}".format(action, " ".join(packages)), flush=True)
    subprocess.run(["yum", "shell", name, "-y"], check=False)
    with suppress(FileNotFoundError):
        os.remove(name)
    print("Finished")


if __name__ == "__main__":
    main()