mirror of
https://github.com/HorlogeSkynet/archey4
synced 2024-11-24 04:00:10 +01:00
923990c3c6
Replaces callback with property
75 lines
2.6 KiB
Python
75 lines
2.6 KiB
Python
"""Custom entry class"""
|
|
|
|
import logging
|
|
import os
|
|
import stat
|
|
from contextlib import suppress
|
|
from functools import cached_property
|
|
from subprocess import DEVNULL, PIPE, CalledProcessError, run
|
|
from typing import List, Union
|
|
|
|
from archey.configuration import Configuration
|
|
from archey.entry import Entry
|
|
|
|
|
|
class Custom(Entry):
|
|
"""Custom entry gathering info based on configuration options"""
|
|
|
|
def __new__(cls, *_, **kwargs):
|
|
# Don't load this entry if a configuration file has too broad permissions.
|
|
# We want to mitigate LPE attacks, as arbitrary commands could be run from a configuration
|
|
# file under another user's control (with write permissions).
|
|
geteuid = getattr(os, "geteuid", None)
|
|
for config_path, stat_info in Configuration().get_config_files_info().items():
|
|
if (
|
|
stat_info.st_uid != 0 and geteuid is not None and stat_info.st_uid != geteuid()
|
|
) or stat_info.st_mode & (stat.S_IWGRP | stat.S_IWOTH):
|
|
logging.getLogger(cls.__module__).warning(
|
|
"Not loading %s entry as %s config file has too broad permissions (%s).",
|
|
cls.__name__,
|
|
config_path,
|
|
stat.filemode(stat_info.st_mode),
|
|
)
|
|
return None
|
|
|
|
return super().__new__(cls, **kwargs)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
|
|
command: Union[str, List[str]]
|
|
|
|
shell = self.options.get("shell", False)
|
|
if shell:
|
|
command = self.options["command"]
|
|
else:
|
|
command = self.options["command"]
|
|
|
|
log_stderr = self.options.get("log_stderr", True)
|
|
|
|
with suppress(CalledProcessError):
|
|
proc = run(
|
|
command,
|
|
stdout=PIPE,
|
|
stderr=PIPE if log_stderr else DEVNULL,
|
|
shell=shell,
|
|
check=self.options.get("check", True),
|
|
universal_newlines=True,
|
|
)
|
|
if proc.stdout:
|
|
self.value = proc.stdout.rstrip().splitlines()
|
|
|
|
if log_stderr and proc.stderr:
|
|
self._logger.warning("%s", proc.stderr.rstrip())
|
|
|
|
@cached_property
|
|
def pretty_value(self) -> [(str, str)]:
|
|
if not self.value:
|
|
return [(self.name, self._default_strings.get("not_detected"))]
|
|
|
|
# Join the results only if `one_line` option is enabled.
|
|
if self.options.get("one_line", True):
|
|
return [(self.name, ", ".join(self.value))]
|
|
else:
|
|
return map(lambda element: (self.name, element), self.value)
|