Building on the config handler I suggest to implement the backwards compatible request parser and config mixer like (maybe in muuntaa/process.py
or the usual muuntaa/cli.py
):
import argparse
import logging
import pathlib
import sys
from typing import Union
import muuntaa.config as cfg
Pathlike = Union[pathlib.Path, str]
LogLevel = int
ScopedMessage = tuple[LogLevel, str]
ConfigType = dict[str, Union[None, bool, int, float, str]]
APP_ALIAS = 'muuntaa'
APP_NAME = 'Converts CVRF 1.2 XML input into CSAF 2.0 JSON output.'
MAGIC_CMD_ARG_ENTERED = 'cmd-arg-entered'
INPUT_FILE_KEY = 'input_file'
OVERWRITABLE_KEYS = [
'fix_insert_current_version_into_revision_history',
'force_insert_default_reference_category',
'remove_CVSS_values_without_vector',
'force',
]
VERSION = '2024.01.09+parent.beeegees'
def parse_request(argv: Union[list[str], None] = None) -> tuple[Union[int, ConfigType], list[ScopedMessage]]:
"""Parse the request as load configuration and mix in (overwerite) command line parameter values."""
if argv is None:
argv = sys.argv[1:]
parser = argparse.ArgumentParser(
prog=APP_ALIAS, description=APP_NAME, formatter_class=argparse.RawTextHelpFormatter
)
# General args
parser.add_argument('-v', '--version', action='version', version=VERSION)
parser.add_argument(
'--input-file', dest='input_file', type=str, required=True, help='CVRF XML input file to parse', metavar='PATH'
)
parser.add_argument(
'--output-dir',
dest='output_dir',
type=str,
default='./',
metavar='PATH',
help='CSAF output dir to write to.' ' Filename is derived from /document/tracking/id.',
)
parser.add_argument(
'--print',
dest='print',
action='store_true',
default=False,
help='Additionally prints CSAF JSON output on stdout.',
)
parser.add_argument(
'--force',
action='store_const',
const='cmd-arg-entered',
help=(
'If used, the converter produces output even if it is invalid (errors occured during conversion).\n'
'Target use case: best-effort conversion to JSON, fix the errors manually, e.g. in Secvisogram.'
),
)
# Document Publisher args
parser.add_argument('--publisher-name', dest='publisher_name', type=str, help='Name of the publisher.')
parser.add_argument(
'--publisher-namespace',
dest='publisher_namespace',
type=str,
help='Namespace of the publisher. Must be a valid URI',
)
# Document Tracking args
parser.add_argument(
'--fix-insert-current-version-into-revision-history',
action='store_const',
const='cmd-arg-entered',
help=(
'If the current version is not present in the revision history the current version is\n'
'added to the revision history. Also warning is produced. By default, an error is produced.'
),
)
# Document References args
parser.add_argument(
'--force-insert-default-reference-category',
action='store_const',
const='cmd-arg-entered',
help="When 'Type' attribute not present in 'Reference' element, then force using default value 'external'.",
)
# Vulnerabilities args
parser.add_argument(
'--remove-CVSS-values-without-vector',
action='store_const',
const='cmd-arg-entered',
help=(
'If vector is not present in CVSS ScoreSet,\n'
'the convertor removes the whole ScoreSet instead of producing an error.'
),
)
parser.add_argument(
'--default-CVSS3-version',
dest='default_CVSS3_version',
help=(
'Default version used for CVSS version 3, when the version cannot be derived from other sources.\n'
"Default value is '3.0'."
),
)
try:
args = {k: v for k, v in vars(parser.parse_args(argv)).items() if v is not None}
except SystemExit as err:
return int(str(err)), []
config = cfg.load()
scoped_messages = cfg.boolify(config)
for scope, message in scoped_messages:
logging.logger.log(scope, message)
if scope >= logging.CRITICAL:
return 1, []
config.update(args) # Update and overwrite config file values with the ones from command line arguments
for key in OVERWRITABLE_KEYS: # Boolean optional arguments that are also present in config need special treatment
if config.get(key) == MAGIC_CMD_ARG_ENTERED:
config[key] = True
if not pathlib.Path(config.get(INPUT_FILE_KEY)).is_file():
logging.logger.log(logging.CRITICAL, f'Input file not found, check the path: {config.get(INPUT_FILE_KEY)}')
return 1, []
return config, []