Kerala Cyber
Warriors
KCW Uploader V1.1
import re
import textwrap
from typing import ( # noqa: F401
Dict,
List,
NamedTuple,
Optional,
Set,
Tuple,
Union,
)
from uaclient import apt, exceptions, messages, system, util
from uaclient.actions import attach_with_token, enable_entitlement_by_name
from uaclient.api.u.pro.attach.magic.initiate.v1 import _initiate
from uaclient.api.u.pro.attach.magic.revoke.v1 import (
MagicAttachRevokeOptions,
_revoke,
)
from uaclient.api.u.pro.attach.magic.wait.v1 import (
MagicAttachWaitOptions,
_wait,
)
from uaclient.api.u.pro.security.fix._common import (
CVE_OR_USN_REGEX,
FixStatus,
UnfixedPackage,
status_message,
)
from uaclient.api.u.pro.security.fix._common.plan.v1 import ( # noqa: F401
ESM_APPS_POCKET,
ESM_INFRA_POCKET,
STANDARD_UPDATES_POCKET,
FixPlanAptUpgradeStep,
FixPlanAttachStep,
FixPlanEnableStep,
FixPlanNoOpAlreadyFixedStep,
FixPlanNoOpLivepatchFixStep,
FixPlanNoOpStatus,
FixPlanNoOpStep,
FixPlanResult,
FixPlanStep,
FixPlanUSNResult,
FixPlanWarning,
FixPlanWarningFailUpdatingESMCache,
FixPlanWarningPackageCannotBeInstalled,
FixPlanWarningSecurityIssueNotFixed,
NoOpAlreadyFixedData,
NoOpLivepatchFixData,
USNAdditionalData,
)
from uaclient.api.u.pro.security.fix.cve.plan.v1 import CVEFixPlanOptions
from uaclient.api.u.pro.security.fix.cve.plan.v1 import _plan as cve_plan
from uaclient.api.u.pro.security.fix.usn.plan.v1 import USNFixPlanOptions
from uaclient.api.u.pro.security.fix.usn.plan.v1 import _plan as usn_plan
from uaclient.api.u.pro.status.is_attached.v1 import (
ContractExpiryStatus,
_is_attached,
)
from uaclient.cli.commands import ProArgument, ProArgumentGroup, ProCommand
from uaclient.cli.detach import action_detach
from uaclient.cli.parser import HelpCategory
from uaclient.clouds.identity import (
CLOUD_TYPE_TO_TITLE,
PRO_CLOUD_URLS,
get_cloud_type,
)
from uaclient.config import UAConfig
from uaclient.defaults import PRINT_WRAP_WIDTH
from uaclient.entitlements import entitlement_factory
from uaclient.entitlements.entitlement_status import (
ApplicabilityStatus,
CanEnableFailure,
UserFacingStatus,
)
from uaclient.files import notices
from uaclient.files.notices import Notice
from uaclient.messages.urls import PRO_HOME_PAGE
from uaclient.status import colorize_commands
class FixContext:
def __init__(
self,
title: str,
dry_run: bool,
affected_pkgs: List[str],
cfg: UAConfig,
):
self.pkg_index = 0
self.unfixed_pkgs = [] # type: List[UnfixedPackage]
self.installed_pkgs = set() # type: Set[str]
self.fix_status = FixStatus.SYSTEM_NON_VULNERABLE
self.title = title
self.affected_pkgs = affected_pkgs
self.dry_run = dry_run
self.cfg = cfg
self.should_print_pkg_header = True
self.warn_package_cannot_be_installed = False
self.fixed_by_livepatch = False
def print_fix_header(self):
if self.affected_pkgs:
msg = messages.SECURITY_AFFECTED_PKGS.pluralize(
len(self.affected_pkgs)
).format(
count=len(self.affected_pkgs),
pkgs=", ".join(sorted(self.affected_pkgs)),
)
print(
textwrap.fill(
msg,
width=PRINT_WRAP_WIDTH,
subsequent_indent=" ",
replace_whitespace=False,
)
)
def print_pkg_header(
self,
source_pkgs: List[str],
status: str,
pocket: Optional[str] = None,
):
if self.should_print_pkg_header:
print(
_format_packages_message(
pkg_list=source_pkgs,
status=status,
pkg_index=self.pkg_index,
num_pkgs=len(self.affected_pkgs),
pocket_source=(
get_pocket_description(pocket) if pocket else None
),
)
)
def add_unfixed_packages(self, pkgs: List[str], unfixed_reason: str):
for pkg in pkgs:
self.unfixed_pkgs.append(
UnfixedPackage(pkg=pkg, unfixed_reason=unfixed_reason)
)
def print_cve_header(cve: FixPlanResult):
lines = [
"{issue}: {description}".format(
issue=cve.title.upper(), description=cve.description
),
" - https://ubuntu.com/security/{}".format(cve.title.upper()),
]
print("\n".join(lines))
def print_usn_header(fix_plan: FixPlanUSNResult):
target_usn = fix_plan.target_usn_plan
lines = [
"{issue}: {description}".format(
issue=target_usn.title.upper(), description=target_usn.description
),
]
additional_data = target_usn.additional_data
if isinstance(additional_data, USNAdditionalData):
if additional_data.associated_cves:
lines.append(messages.SECURITY_FOUND_CVES)
for cve in additional_data.associated_cves:
lines.append(
" - {}".format(
messages.urls.SECURITY_CVE_PAGE.format(cve=cve)
)
)
elif additional_data.associated_launchpad_bugs:
lines.append(messages.SECURITY_FOUND_LAUNCHPAD_BUGS)
for lp_bug in additional_data.associated_launchpad_bugs:
lines.append(" - " + lp_bug)
print("\n".join(lines))
def fix_cve(security_issue: str, dry_run: bool, cfg: UAConfig):
fix_plan = cve_plan(
options=CVEFixPlanOptions(cves=[security_issue]), cfg=cfg
)
error = fix_plan.cves_data.cves[0].error
if error and error.msg:
raise exceptions.AnonymousUbuntuProError(
named_msg=messages.NamedMessage(
error.code or "unexpected-error", error.msg
)
)
print_cve_header(fix_plan.cves_data.cves[0])
print()
status, _ = execute_fix_plan(fix_plan.cves_data.cves[0], dry_run, cfg)
return status
def fix_usn(
security_issue: str, dry_run: bool, no_related: bool, cfg: UAConfig
):
fix_plan = usn_plan(
options=USNFixPlanOptions(usns=[security_issue]), cfg=cfg
)
error = fix_plan.usns_data.usns[0].target_usn_plan.error
if error and error.msg:
raise exceptions.AnonymousUbuntuProError(
named_msg=messages.NamedMessage(
error.code or "unexpected-error", error.msg
)
)
print_usn_header(fix_plan.usns_data.usns[0])
print(
"\n"
+ messages.SECURITY_FIXING_REQUESTED_USN.format(
issue_id=security_issue
)
)
target_usn_status, _ = execute_fix_plan(
fix_plan.usns_data.usns[0].target_usn_plan,
dry_run,
cfg,
)
if target_usn_status not in (
FixStatus.SYSTEM_NON_VULNERABLE,
FixStatus.SYSTEM_NOT_AFFECTED,
):
return target_usn_status
related_usns_plan = fix_plan.usns_data.usns[0].related_usns_plan
if not related_usns_plan or no_related:
return target_usn_status
print(
"\n"
+ messages.SECURITY_RELATED_USNS.format(
related_usns="\n- ".join(usn.title for usn in related_usns_plan)
)
)
print("\n" + messages.SECURITY_FIXING_RELATED_USNS)
related_usn_status = (
{}
) # type: Dict[str, Tuple[FixStatus, List[UnfixedPackage]]]
for related_usn_plan in related_usns_plan:
print("- {}".format(related_usn_plan.title))
related_usn_status[related_usn_plan.title] = execute_fix_plan(
related_usn_plan,
dry_run,
cfg,
)
print()
print(messages.SECURITY_USN_SUMMARY)
_handle_fix_status_message(
target_usn_status,
security_issue,
context=messages.FIX_ISSUE_CONTEXT_REQUESTED,
)
failure_on_related_usn = False
for related_usn_plan in related_usns_plan:
status, unfixed_pkgs = related_usn_status[related_usn_plan.title]
_handle_fix_status_message(
status,
related_usn_plan.title,
context=messages.FIX_ISSUE_CONTEXT_RELATED,
)
if status == FixStatus.SYSTEM_VULNERABLE_UNTIL_REBOOT:
print(
"- "
+ messages.ENABLE_REBOOT_REQUIRED_TMPL.format(
operation="fix operation"
)
)
failure_on_related_usn = True
if status == FixStatus.SYSTEM_STILL_VULNERABLE:
for unfixed_pkg in unfixed_pkgs:
if unfixed_pkg.unfixed_reason:
print(
" - {}: {}".format(
unfixed_pkg.pkg, unfixed_pkg.unfixed_reason
)
)
failure_on_related_usn = True
if failure_on_related_usn:
print(
"\n"
+ messages.SECURITY_RELATED_USN_ERROR.format(
issue_id=security_issue
)
)
return target_usn_status
def _format_packages_message(
pkg_list: List[str],
status: str,
pkg_index: int,
num_pkgs: int,
pocket_source: Optional[str] = None,
) -> str:
"""Format the packages and status to an user friendly message."""
if not pkg_list:
return ""
msg_index = []
src_pkgs = []
for src_pkg in pkg_list:
pkg_index += 1
msg_index.append("{}/{}".format(pkg_index, num_pkgs))
src_pkgs.append(src_pkg)
msg_header = textwrap.fill(
"{} {}:".format(
"(" + ", ".join(msg_index) + ")", ", ".join(sorted(src_pkgs))
),
width=PRINT_WRAP_WIDTH,
subsequent_indent=" ",
)
return "{}\n{}".format(msg_header, status_message(status, pocket_source))
def _run_ua_attach(cfg: UAConfig, token: str) -> bool:
"""Attach to an Ubuntu Pro subscription with a given token.
:return: True if attach performed without errors.
"""
print(colorize_commands([["pro", "attach", token]]))
try:
attach_with_token(cfg, token=token, allow_enable=True)
return True
except exceptions.UbuntuProError as err:
print(err.msg)
return False
def _inform_ubuntu_pro_existence_if_applicable() -> None:
"""Alert the user when running Pro on cloud with PRO support."""
cloud_type, _ = get_cloud_type()
if cloud_type in PRO_CLOUD_URLS.keys():
print(
messages.SECURITY_USE_PRO_TMPL.format(
title=CLOUD_TYPE_TO_TITLE.get(cloud_type),
cloud_specific_url=PRO_CLOUD_URLS.get(cloud_type),
)
)
def _perform_magic_attach(cfg: UAConfig):
print(messages.CLI_MAGIC_ATTACH_INIT)
initiate_resp = _initiate(cfg=cfg)
print(
"\n"
+ messages.CLI_MAGIC_ATTACH_SIGN_IN.format(
user_code=initiate_resp.user_code
)
)
wait_options = MagicAttachWaitOptions(magic_token=initiate_resp.token)
try:
wait_resp = _wait(options=wait_options, cfg=cfg)
except exceptions.MagicAttachTokenError as e:
print(messages.CLI_MAGIC_ATTACH_FAILED)
revoke_options = MagicAttachRevokeOptions(
magic_token=initiate_resp.token
)
_revoke(options=revoke_options, cfg=cfg)
raise e
print("\n" + messages.CLI_MAGIC_ATTACH_PROCESSING)
return _run_ua_attach(cfg, wait_resp.contract_token)
def _prompt_for_attach(cfg: UAConfig) -> bool:
"""Prompt for attach to a subscription or token.
:return: True if attach performed.
"""
_inform_ubuntu_pro_existence_if_applicable()
print(messages.SECURITY_UPDATE_NOT_INSTALLED_SUBSCRIPTION)
choice = util.prompt_choices(
messages.SECURITY_FIX_ATTACH_PROMPT,
valid_choices=["s", "a", "c"],
)
if choice == "c":
return False
if choice == "s":
return _perform_magic_attach(cfg)
if choice == "a":
print(messages.PROMPT_ENTER_TOKEN)
token = input("> ")
return _run_ua_attach(cfg, token)
return True
def _format_unfixed_packages_msg(unfixed_pkgs: List[str]) -> str:
"""Format the list of unfixed packages into an message.
:returns: A string containing the message output for the unfixed
packages.
"""
num_pkgs_unfixed = len(unfixed_pkgs)
return textwrap.fill(
messages.SECURITY_PKG_STILL_AFFECTED.pluralize(
num_pkgs_unfixed
).format(
num_pkgs=num_pkgs_unfixed,
pkgs=", ".join(sorted(unfixed_pkgs)),
),
width=PRINT_WRAP_WIDTH,
subsequent_indent=" ",
)
def _check_subscription_is_expired(cfg: UAConfig, dry_run: bool) -> bool:
"""Check if the Ubuntu Pro subscription is expired.
:returns: True if subscription is expired and not renewed.
"""
contract_expiry_status = _is_attached(cfg).contract_status
if (
contract_expiry_status
and contract_expiry_status == ContractExpiryStatus.EXPIRED.value
):
if dry_run:
print(messages.SECURITY_DRY_RUN_UA_EXPIRED_SUBSCRIPTION)
return False
return True
return False
def _prompt_for_new_token(cfg: UAConfig) -> bool:
"""Prompt for attach a new subscription token to the user.
:return: True if attach performed.
"""
import argparse
_inform_ubuntu_pro_existence_if_applicable()
print(messages.SECURITY_UPDATE_NOT_INSTALLED_EXPIRED)
choice = util.prompt_choices(
messages.SECURITY_FIX_RENEW_PROMPT.format(url=PRO_HOME_PAGE),
valid_choices=["r", "c"],
)
if choice == "r":
print(messages.PROMPT_EXPIRED_ENTER_TOKEN)
token = input("> ")
print(colorize_commands([["pro", "detach"]]))
action_detach(argparse.Namespace(assume_yes=True, format="cli"), cfg)
return _run_ua_attach(cfg, token)
return False
def _prompt_for_enable(cfg: UAConfig, service: str) -> bool:
"""Prompt for enable a pro service.
:return: True if enable performed.
"""
print(messages.SECURITY_SERVICE_DISABLED.format(service=service))
choice = util.prompt_choices(
messages.SECURITY_FIX_ENABLE_PROMPT.format(service=service),
valid_choices=["e", "c"],
)
if choice == "e":
print(colorize_commands([["pro", "enable", service]]))
ret, reason = enable_entitlement_by_name(cfg=cfg, name=service)
if (
not ret
and reason is not None
and isinstance(reason, CanEnableFailure)
):
if reason.message is not None:
print(reason.message.msg)
return ret
return False
def _handle_subscription_for_required_service(
service: str, cfg: UAConfig, dry_run: bool
) -> bool:
"""
Verify if the Ubuntu Pro subscription has the required service enabled.
"""
ent = entitlement_factory(cfg=cfg, name=service)
if ent:
ent_status, _ = ent.user_facing_status()
if ent_status == UserFacingStatus.ACTIVE:
return True
applicability_status, _ = ent.applicability_status()
if applicability_status == ApplicabilityStatus.APPLICABLE:
if dry_run:
print(
"\n"
+ messages.SECURITY_DRY_RUN_UA_SERVICE_NOT_ENABLED.format(
service=ent.name
)
)
return True
if _prompt_for_enable(cfg, ent.name):
return True
else:
print(
messages.SECURITY_UA_SERVICE_NOT_ENABLED.format(
service=ent.name
)
)
else:
print(
messages.SECURITY_UA_SERVICE_NOT_ENTITLED.format(
service=ent.name
)
)
return False
def _handle_fix_status_message(
status: FixStatus, issue_id: str, context: str = ""
):
if status == FixStatus.SYSTEM_NON_VULNERABLE:
if context:
msg = messages.SECURITY_ISSUE_RESOLVED_ISSUE_CONTEXT.format(
issue=issue_id, context=context
)
else:
msg = messages.SECURITY_ISSUE_RESOLVED.format(issue=issue_id)
print(util.handle_unicode_characters(msg))
elif status == FixStatus.SYSTEM_NOT_AFFECTED:
if context:
msg = messages.SECURITY_ISSUE_UNAFFECTED_ISSUE_CONTEXT.format(
issue=issue_id, context=context
)
else:
msg = messages.SECURITY_ISSUE_UNAFFECTED.format(issue=issue_id)
print(util.handle_unicode_characters(msg))
elif status == FixStatus.SYSTEM_VULNERABLE_UNTIL_REBOOT:
if context:
msg = messages.SECURITY_ISSUE_NOT_RESOLVED_ISSUE_CONTEXT.format(
issue=issue_id, context=context
)
else:
msg = messages.SECURITY_ISSUE_NOT_RESOLVED.format(issue=issue_id)
print(util.handle_unicode_characters(msg))
else:
if context:
msg = messages.SECURITY_ISSUE_NOT_RESOLVED_ISSUE_CONTEXT.format(
issue=issue_id, context=context
)
else:
msg = messages.SECURITY_ISSUE_NOT_RESOLVED.format(issue=issue_id)
print(util.handle_unicode_characters(msg))
def get_pocket_description(pocket: str):
if pocket == STANDARD_UPDATES_POCKET:
return messages.SECURITY_UBUNTU_STANDARD_UPDATES_POCKET
elif pocket == ESM_INFRA_POCKET:
return messages.SECURITY_UA_INFRA_POCKET
elif pocket == ESM_APPS_POCKET:
return messages.SECURITY_UA_APPS_POCKET
else:
return pocket
def _execute_package_cannot_be_installed_step(
fix_context: FixContext,
step: FixPlanWarningPackageCannotBeInstalled,
):
fix_context.print_pkg_header(
source_pkgs=step.data.related_source_packages,
status="released",
pocket=step.data.pocket,
)
fix_context.should_print_pkg_header = False
warn_msg = messages.FIX_CANNOT_INSTALL_PACKAGE.format(
package=step.data.binary_package,
version=step.data.binary_package_version,
)
print("- " + warn_msg)
fix_context.add_unfixed_packages(
pkgs=[step.data.source_package], unfixed_reason=warn_msg
)
fix_context.warn_package_cannot_be_installed = True
fix_context.fix_status = FixStatus.SYSTEM_STILL_VULNERABLE
def _execute_security_issue_not_fixed_step(
fix_context: FixContext, step: FixPlanWarningSecurityIssueNotFixed
):
fix_context.print_pkg_header(
source_pkgs=step.data.source_packages,
status=step.data.status,
)
fix_context.pkg_index += len(step.data.source_packages)
fix_context.add_unfixed_packages(
pkgs=step.data.source_packages,
unfixed_reason=status_message(step.data.status),
)
fix_context.fix_status = FixStatus.SYSTEM_STILL_VULNERABLE
def _execute_fail_updating_esm_cache_step(
fix_context: FixContext, step: FixPlanWarningFailUpdatingESMCache
):
if util.we_are_currently_root():
print(messages.CLI_FIX_FAIL_UPDATING_ESM_CACHE)
else:
print("\n" + messages.CLI_FIX_FAIL_UPDATING_ESM_CACHE_NON_ROOT + "\n")
def _execute_apt_upgrade_step(
fix_context: FixContext,
step: FixPlanAptUpgradeStep,
):
fix_context.print_pkg_header(
source_pkgs=step.data.source_packages,
status="released",
pocket=step.data.pocket,
)
fix_context.pkg_index += len(step.data.source_packages)
if not step.data.binary_packages:
if not fix_context.warn_package_cannot_be_installed:
print(messages.SECURITY_UPDATE_INSTALLED)
fix_context.fix_status = FixStatus.SYSTEM_NON_VULNERABLE
return
if not util.we_are_currently_root() and not fix_context.dry_run:
print(messages.SECURITY_APT_NON_ROOT)
fix_context.fix_status = FixStatus.SYSTEM_STILL_VULNERABLE
fix_context.add_unfixed_packages(
pkgs=step.data.source_packages,
unfixed_reason=messages.SECURITY_APT_NON_ROOT,
)
return
print(
colorize_commands(
[
["apt", "update", "&&"]
+ ["apt", "install", "--only-upgrade", "-y"]
+ sorted(step.data.binary_packages)
]
)
)
if fix_context.dry_run:
fix_context.fix_status = FixStatus.SYSTEM_NON_VULNERABLE
return
try:
apt.run_apt_update_command()
apt.run_apt_command(
cmd=["apt-get", "install", "--only-upgrade", "-y"]
+ step.data.binary_packages,
override_env_vars={"DEBIAN_FRONTEND": "noninteractive"},
)
except Exception as e:
msg = getattr(e, "msg", str(e))
print(msg)
fix_context.fix_status = FixStatus.SYSTEM_STILL_VULNERABLE
fix_context.add_unfixed_packages(
pkgs=step.data.source_packages,
unfixed_reason=msg,
)
return
fix_context.fix_status = FixStatus.SYSTEM_NON_VULNERABLE
fix_context.should_print_pkg_header = True
fix_context.installed_pkgs.update(step.data.binary_packages)
def _execute_attach_step(
fix_context: FixContext,
step: FixPlanAttachStep,
):
pocket = (
ESM_INFRA_POCKET
if step.data.required_service == "esm-infra"
else ESM_APPS_POCKET
)
fix_context.print_pkg_header(
source_pkgs=step.data.source_packages,
status="released",
pocket=pocket,
)
fix_context.should_print_pkg_header = False
if not _is_attached(fix_context.cfg).is_attached:
if fix_context.dry_run:
print("\n" + messages.SECURITY_DRY_RUN_UA_NOT_ATTACHED)
else:
if not _prompt_for_attach(fix_context.cfg):
fix_context.fix_status = FixStatus.SYSTEM_STILL_VULNERABLE
fix_context.add_unfixed_packages(
pkgs=step.data.source_packages,
unfixed_reason=messages.SECURITY_UA_SERVICE_REQUIRED.format( # noqa
service=step.data.required_service
),
)
return
elif _check_subscription_is_expired(
cfg=fix_context.cfg, dry_run=fix_context.dry_run
):
if fix_context.dry_run:
print(messages.SECURITY_DRY_RUN_UA_EXPIRED_SUBSCRIPTION)
elif not _prompt_for_new_token(fix_context.cfg):
fix_context.fix_status = FixStatus.SYSTEM_STILL_VULNERABLE
fix_context.add_unfixed_packages(
pkgs=step.data.source_packages,
unfixed_reason=messages.SECURITY_UA_SERVICE_WITH_EXPIRED_SUB.format( # noqa
service=step.data.required_service
),
)
return
fix_context.fix_status = FixStatus.SYSTEM_NON_VULNERABLE
def _execute_enable_step(
fix_context: FixContext,
step: FixPlanEnableStep,
):
pocket = (
ESM_INFRA_POCKET
if step.data.service == "esm-infra"
else ESM_APPS_POCKET
)
fix_context.print_pkg_header(
source_pkgs=step.data.source_packages,
status="released",
pocket=pocket,
)
fix_context.should_print_pkg_header = False
if not _handle_subscription_for_required_service( # noqa
step.data.service,
fix_context.cfg,
fix_context.dry_run,
):
fix_context.add_unfixed_packages(
pkgs=step.data.source_packages,
unfixed_reason=messages.SECURITY_UA_SERVICE_NOT_ENABLED_SHORT.format( # noqa
service=step.data.service
),
)
fix_context.fix_status = FixStatus.SYSTEM_STILL_VULNERABLE
return
return FixStatus.SYSTEM_NON_VULNERABLE
def _execute_noop_not_affected_step(
fix_context: FixContext, step: FixPlanNoOpStep
):
if step.data.status == FixPlanNoOpStatus.NOT_AFFECTED.value:
print(messages.SECURITY_NO_AFFECTED_PKGS)
fix_context.fix_status = FixStatus.SYSTEM_NOT_AFFECTED
def _execute_noop_fixed_by_livepatch_step(
fix_context: FixContext, step: FixPlanNoOpLivepatchFixStep
):
if isinstance(step.data, NoOpLivepatchFixData):
print(
messages.CVE_FIXED_BY_LIVEPATCH.format(
issue=fix_context.title,
version=step.data.patch_version,
)
)
fix_context.fixed_by_livepatch = True
def _execute_noop_already_fixed_step(
fix_context: FixContext, step: FixPlanNoOpAlreadyFixedStep
):
if isinstance(step.data, NoOpAlreadyFixedData):
fix_context.print_pkg_header(
source_pkgs=step.data.source_packages,
status="released",
pocket=step.data.pocket,
)
print(messages.SECURITY_UPDATE_INSTALLED)
fix_context.pkg_index += len(step.data.source_packages)
def execute_fix_plan(
fix_plan: FixPlanResult, dry_run: bool, cfg: UAConfig
) -> Tuple[FixStatus, List[UnfixedPackage]]:
full_plan = [
*fix_plan.plan,
*fix_plan.warnings,
] # type: List[Union[FixPlanStep, FixPlanWarning]]
fix_context = FixContext(
title=fix_plan.title,
dry_run=dry_run,
affected_pkgs=fix_plan.affected_packages or [],
cfg=cfg,
)
fix_context.print_fix_header()
for step in sorted(full_plan, key=lambda x: x.order):
if isinstance(step, FixPlanWarningPackageCannotBeInstalled):
_execute_package_cannot_be_installed_step(fix_context, step)
if isinstance(step, FixPlanWarningSecurityIssueNotFixed):
_execute_security_issue_not_fixed_step(fix_context, step)
if isinstance(step, FixPlanWarningFailUpdatingESMCache):
_execute_fail_updating_esm_cache_step(fix_context, step)
if isinstance(step, FixPlanAptUpgradeStep):
_execute_apt_upgrade_step(fix_context, step)
if fix_context.fix_status != FixStatus.SYSTEM_NON_VULNERABLE:
break
if isinstance(step, FixPlanAttachStep):
_execute_attach_step(fix_context, step)
if fix_context.fix_status != FixStatus.SYSTEM_NON_VULNERABLE:
break
if isinstance(step, FixPlanEnableStep):
_execute_enable_step(fix_context, step)
if fix_context.fix_status != FixStatus.SYSTEM_NON_VULNERABLE:
break
if isinstance(step, FixPlanNoOpStep):
_execute_noop_not_affected_step(fix_context, step)
if isinstance(step, FixPlanNoOpLivepatchFixStep):
_execute_noop_fixed_by_livepatch_step(fix_context, step)
if isinstance(step, FixPlanNoOpAlreadyFixedStep):
_execute_noop_already_fixed_step(fix_context, step)
print()
if fix_context.unfixed_pkgs:
print(
_format_unfixed_packages_msg(
list(
set(
[
unfixed_pkg.pkg
for unfixed_pkg in fix_context.unfixed_pkgs
]
)
)
)
)
fix_context.fix_status = FixStatus.SYSTEM_STILL_VULNERABLE
if (
fix_context.fix_status == FixStatus.SYSTEM_NON_VULNERABLE
and system.should_reboot(installed_pkgs=fix_context.installed_pkgs)
):
fix_context.fix_status = FixStatus.SYSTEM_VULNERABLE_UNTIL_REBOOT
reboot_msg = messages.ENABLE_REBOOT_REQUIRED_TMPL.format(
operation="fix operation"
)
print(reboot_msg)
notices.add(
Notice.ENABLE_REBOOT_REQUIRED,
operation="fix operation",
)
if not fix_context.fixed_by_livepatch:
_handle_fix_status_message(fix_context.fix_status, fix_plan.title)
return (fix_context.fix_status, fix_context.unfixed_pkgs)
def action_fix(args, *, cfg, **kwargs):
if not re.match(CVE_OR_USN_REGEX, args.security_issue):
raise exceptions.InvalidSecurityIssueIdFormat(
issue=args.security_issue
)
if args.dry_run:
print(messages.SECURITY_DRY_RUN_WARNING)
if "cve" in args.security_issue.lower():
status = fix_cve(args.security_issue, args.dry_run, cfg)
else:
status = fix_usn(
args.security_issue, args.dry_run, args.no_related, cfg
)
return status.exit_code
fix_command = ProCommand(
"fix",
help=messages.CLI_ROOT_FIX,
description=messages.CLI_FIX_DESC,
action=action_fix,
help_category=HelpCategory.SECURITY,
argument_groups=[
ProArgumentGroup(
arguments=[
ProArgument("security_issue", help=messages.CLI_FIX_ISSUE),
ProArgument(
"--dry-run",
help=messages.CLI_FIX_DRY_RUN,
action="store_true",
),
ProArgument(
"--no-related",
help=messages.CLI_FIX_NO_RELATED,
action="store_true",
),
]
)
],
)
-=[ KCW uplo4d3r c0ded by cJ_n4p573r ]=-
Ⓒ2017 ҠЄГѦLѦ СүѣЄГ ЩѦГГіѺГՏ