Coverage for csaf/cli.py: 79.03%
48 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-18 20:12:48 +00:00
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-18 20:12:48 +00:00
1"""Commandline API gateway for csaf."""
3import sys
4from typing import List, Mapping
6import typer
8import csaf
9import csaf.config as cfg
10import csaf.csaf as lint
11import csaf.env as env
12from csaf import log
14app = typer.Typer(
15 add_completion=False,
16 context_settings={'help_option_names': ['-h', '--help']},
17 no_args_is_help=True,
18)
21@app.callback(invoke_without_command=True)
22def callback(
23 version: bool = typer.Option(
24 False,
25 '-V',
26 '--version',
27 help='Display the csaf version and exit',
28 is_eager=True,
29 )
30) -> None:
31 """
32 Common Security Advisory Framework (CSAF) Verification and Validation.
33 """
34 if version:
35 typer.echo(f'{csaf.APP_NAME} version {csaf.__version__}')
36 raise typer.Exit()
39@app.command('template')
40def app_template() -> int:
41 """
42 Write a template of a well-formed JSON configuration to standard out and exit
44 The strategy for looking up configurations is to start at the current working
45 directory trying to read a file with the name `.csaf.json` else try to read
46 same named file in the user folder (home).
48 In case an explicit path is given to the config option of commands that offer
49 it, only that path is considered.
50 """
51 sys.stdout.write(cfg.generate_template())
52 return sys.exit(0)
55@app.command('report')
56def report() -> int:
57 """Output the report of the environment for support."""
58 sys.stdout.write(env.report())
59 return sys.exit(0)
62@app.command('validate')
63def validate(
64 source: List[str],
65 inp: str = typer.Option(
66 '',
67 '-i',
68 '--input',
69 help='Path to CSAF input file',
70 metavar='<sourcepath>',
71 ),
72 conf: str = typer.Option(
73 '',
74 '-c',
75 '--config',
76 help=f'Path to config file (default is $HOME/{csaf.DEFAULT_CONFIG_NAME})',
77 metavar='<configpath>',
78 ),
79 bail_out: bool = typer.Option(
80 False,
81 '-b',
82 '--bail-out',
83 help='Bail out (exit) on first failure (default is False)',
84 ),
85 verify: bool = typer.Option(
86 False,
87 '-n',
88 '--dry-run',
89 help='Dry run (default is False)',
90 ),
91 verbose: bool = typer.Option(
92 False,
93 '-v',
94 '--verbose',
95 help='Verbose output (default is False)',
96 ),
97 quiet: bool = typer.Option(
98 False,
99 '-q',
100 '--quiet',
101 help='Minimal output (default is False)',
102 ),
103 strict: bool = typer.Option(
104 False,
105 '-s',
106 '--strict',
107 help='Ouput noisy warnings on console (default is False)',
108 ),
109) -> int:
110 """
111 Common Security Advisory Framework (CSAF) Verification and Validation.
113 You can set some options per environment variables:
115 \b
116 * CSAF_USER='remote-user'
117 * CSAF_TOKEN='remote-secret'
118 * CSAF_BASE_URL='https://csaf.example.com/file/names/below/here/'
119 * CSAF_BAIL_OUT='AnythingTruthy'
120 * CSAF_DEBUG='AnythingTruthy'
121 * CSAF_VERBOSE='AnythingTruthy'
122 * CSAF_STRICT='AnythingTruthy'
124 The quiet option (if given) disables any conflicting verbosity setting.
125 """
126 command = 'validate'
127 transaction_mode = 'commit' if not verify else 'dry-run'
128 if quiet: 128 ↛ 132line 128 didn't jump to line 132 because the condition on line 128 was always true
129 csaf.QUIET = True
130 csaf.DEBUG = False
131 csaf.VERBOSE = False
132 elif verbose:
133 csaf.VERBOSE = True
135 if strict: 135 ↛ 138line 135 didn't jump to line 138 because the condition on line 135 was always true
136 csaf.STRICT = True
138 if bail_out: 138 ↛ 141line 138 didn't jump to line 141 because the condition on line 138 was always true
139 csaf.BAIL_OUT = True
141 if transaction_mode == 'dry-run': 141 ↛ 144line 141 didn't jump to line 144 because the condition on line 141 was always true
142 csaf.DRY_RUN = True
144 configuration = cfg.read_configuration(str(conf)) if conf else {}
146 options: Mapping[str, object] = {
147 'configuration': configuration,
148 'bail_out': bail_out,
149 'quiet': quiet,
150 'strict': strict,
151 'verbose': verbose,
152 }
154 paths = (inp,) if inp else tuple(source)
155 code, message = lint.process(command, transaction_mode, paths[0], options)
156 if message: 156 ↛ 158line 156 didn't jump to line 158 because the condition on line 156 was always true
157 log.error(message)
158 return code
161@app.command('version')
162def app_version() -> None:
163 """
164 Display the csaf version and exit.
165 """
166 callback(True)