Mini Shell
#!/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 main():
print('Starting', flush=True)
# remove packages from HardenedPHP that are not available otherwise
# downgrade remaining HardenedPHP packages
hardened_php_names = repoquery(disablerepo=['*'],
enablerepo=[EA_PHP_HARDENED_REPO])
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()
Zerion Mini Shell 1.0