diff --git a/package/share/bash-completion/completions/_ahriman b/package/share/bash-completion/completions/_ahriman index a35a19fb..c8c584be 100644 --- a/package/share/bash-completion/completions/_ahriman +++ b/package/share/bash-completion/completions/_ahriman @@ -2,7 +2,7 @@ _shtab_ahriman_subparsers=('aur-search' 'search' 'help-commands-unsafe' 'help' 'help-updates' 'help-version' 'version' 'package-add' 'add' 'package-update' 'package-remove' 'remove' 'package-status' 'status' 'package-status-remove' 'package-status-update' 'status-update' 'patch-add' 'patch-list' 'patch-remove' 'patch-set-add' 'repo-backup' 'repo-check' 'check' 'repo-create-keyring' 'repo-create-mirrorlist' 'repo-daemon' 'daemon' 'repo-rebuild' 'rebuild' 'repo-remove-unknown' 'remove-unknown' 'repo-report' 'report' 'repo-restore' 'repo-sign' 'sign' 'repo-status-update' 'repo-sync' 'sync' 'repo-tree' 'repo-triggers' 'repo-update' 'update' 'service-clean' 'clean' 'repo-clean' 'service-config' 'config' 'repo-config' 'service-config-validate' 'config-validate' 'repo-config-validate' 'service-key-import' 'key-import' 'service-repositories' 'service-run' 'run' 'service-setup' 'init' 'repo-init' 'repo-setup' 'setup' 'service-shell' 'shell' 'service-tree-migrate' 'user-add' 'user-list' 'user-remove' 'web') -_shtab_ahriman_option_strings=('-h' '--help' '-a' '--architecture' '-c' '--configuration' '--force' '-l' '--lock' '--log-handler' '-q' '--quiet' '--report' '--no-report' '-r' '--repository' '--unsafe' '--wait-timeout' '-V' '--version') +_shtab_ahriman_option_strings=('-h' '--help' '-a' '--architecture' '-c' '--configuration' '--force' '-l' '--lock' '--log-handler' '-q' '--quiet' '--report' '--no-report' '-r' '--repository' '--unsafe' '-V' '--version' '--wait-timeout') _shtab_ahriman_aur_search_option_strings=('-h' '--help' '-e' '--exit-code' '--info' '--no-info' '--sort-by') _shtab_ahriman_search_option_strings=('-h' '--help' '-e' '--exit-code' '--info' '--no-info' '--sort-by') _shtab_ahriman_help_commands_unsafe_option_strings=('-h' '--help') diff --git a/package/share/man/man1/ahriman.1 b/package/share/man/man1/ahriman.1 index 17685b8f..5b874c61 100644 --- a/package/share/man/man1/ahriman.1 +++ b/package/share/man/man1/ahriman.1 @@ -1,9 +1,9 @@ -.TH AHRIMAN "1" "2023\-11\-06" "ahriman" "Generated Python Manual" +.TH AHRIMAN "1" "2023\-11\-12" "ahriman" "Generated Python Manual" .SH NAME ahriman .SH SYNOPSIS .B ahriman -[-h] [-a ARCHITECTURE] [-c CONFIGURATION] [--force] [-l LOCK] [--log-handler {console,syslog,journald}] [-q] [--report | --no-report] [-r REPOSITORY] [--unsafe] [--wait-timeout WAIT_TIMEOUT] [-V] {aur-search,search,help-commands-unsafe,help,help-updates,help-version,version,package-add,add,package-update,package-remove,remove,package-status,status,package-status-remove,package-status-update,status-update,patch-add,patch-list,patch-remove,patch-set-add,repo-backup,repo-check,check,repo-create-keyring,repo-create-mirrorlist,repo-daemon,daemon,repo-rebuild,rebuild,repo-remove-unknown,remove-unknown,repo-report,report,repo-restore,repo-sign,sign,repo-status-update,repo-sync,sync,repo-tree,repo-triggers,repo-update,update,service-clean,clean,repo-clean,service-config,config,repo-config,service-config-validate,config-validate,repo-config-validate,service-key-import,key-import,service-repositories,service-run,run,service-setup,init,repo-init,repo-setup,setup,service-shell,shell,service-tree-migrate,user-add,user-list,user-remove,web} ... +[-h] [-a ARCHITECTURE] [-c CONFIGURATION] [--force] [-l LOCK] [--log-handler {console,syslog,journald}] [-q] [--report | --no-report] [-r REPOSITORY] [--unsafe] [-V] [--wait-timeout WAIT_TIMEOUT] {aur-search,search,help-commands-unsafe,help,help-updates,help-version,version,package-add,add,package-update,package-remove,remove,package-status,status,package-status-remove,package-status-update,status-update,patch-add,patch-list,patch-remove,patch-set-add,repo-backup,repo-check,check,repo-create-keyring,repo-create-mirrorlist,repo-daemon,daemon,repo-rebuild,rebuild,repo-remove-unknown,remove-unknown,repo-report,report,repo-restore,repo-sign,sign,repo-status-update,repo-sync,sync,repo-tree,repo-triggers,repo-update,update,service-clean,clean,repo-clean,service-config,config,repo-config,service-config-validate,config-validate,repo-config-validate,service-key-import,key-import,service-repositories,service-run,run,service-setup,init,repo-init,repo-setup,setup,service-shell,shell,service-tree-migrate,user-add,user-list,user-remove,web} ... .SH DESCRIPTION ArcH linux ReposItory MANager @@ -44,15 +44,15 @@ filter by target repository \fB\-\-unsafe\fR allow to run ahriman as non\-ahriman user. Some actions might be unavailable +.TP +\fB\-V\fR, \fB\-\-version\fR +show program's version number and exit + .TP \fB\-\-wait\-timeout\fR \fI\,WAIT_TIMEOUT\/\fR wait for lock to be free. Negative value will lead to immediate application run even if there is lock file. In case of zero value, the application will wait infinitely -.TP -\fB\-V\fR, \fB\-\-version\fR -show program's version number and exit - .SH COMMAND .TP @@ -208,22 +208,22 @@ sort field by this field. In case if two packages have the same value of the spe by name .SH COMMAND \fI\,'ahriman help\-commands\-unsafe'\/\fR -usage: ahriman help\-commands\-unsafe [\-h] [command ...] +usage: ahriman help\-commands\-unsafe [\-h] [subcommand ...] list unsafe commands as defined in default args .TP -\fBcommand\fR +\fBsubcommand\fR instead of showing commands, just test command line for unsafe subcommand and return 0 in case if command is safe and 1 otherwise .SH COMMAND \fI\,'ahriman help'\/\fR -usage: ahriman help [\-h] [command] +usage: ahriman help [\-h] [subcommand] show help message for application or command and exit .TP -\fBcommand\fR +\fBsubcommand\fR show help message for specific command .SH COMMAND \fI\,'ahriman help\-updates'\/\fR diff --git a/package/share/zsh/site-functions/_ahriman b/package/share/zsh/site-functions/_ahriman index 70e7f5a2..593393eb 100644 --- a/package/share/zsh/site-functions/_ahriman +++ b/package/share/zsh/site-functions/_ahriman @@ -90,8 +90,8 @@ _shtab_ahriman_options=( {--report,--no-report}"[force enable or disable reporting to web service (default\: True)]:report:" {-r,--repository}"[filter by target repository (default\: None)]:repository:" "--unsafe[allow to run ahriman as non-ahriman user. Some actions might be unavailable (default\: False)]" - "--wait-timeout[wait for lock to be free. Negative value will lead to immediate application run even if there is lock file. In case of zero value, the application will wait infinitely (default\: -1)]:wait_timeout:" "(- : *)"{-V,--version}"[show program\'s version number and exit]" + "--wait-timeout[wait for lock to be free. Negative value will lead to immediate application run even if there is lock file. In case of zero value, the application will wait infinitely (default\: -1)]:wait_timeout:" ) _shtab_ahriman_add_options=( diff --git a/src/ahriman/application/ahriman.py b/src/ahriman/application/ahriman.py index 67456a27..1fb12809 100644 --- a/src/ahriman/application/ahriman.py +++ b/src/ahriman/application/ahriman.py @@ -87,11 +87,11 @@ def _parser() -> argparse.ArgumentParser: parser.add_argument("--repository-id", help=argparse.SUPPRESS) parser.add_argument("--unsafe", help="allow to run ahriman as non-ahriman user. Some actions might be unavailable", action="store_true") + parser.add_argument("-V", "--version", action="version", version=__version__) parser.add_argument("--wait-timeout", help="wait for lock to be free. Negative value will lead to " "immediate application run even if there is lock file. " "In case of zero value, the application will wait infinitely", type=int, default=-1) - parser.add_argument("-V", "--version", action="version", version=__version__) subparsers = parser.add_subparsers(title="command", help="command to run", dest="command") @@ -178,8 +178,8 @@ def _set_help_commands_unsafe_parser(root: SubParserAction) -> argparse.Argument """ parser = root.add_parser("help-commands-unsafe", help="list unsafe commands", description="list unsafe commands as defined in default args", formatter_class=_formatter) - parser.add_argument("command", help="instead of showing commands, just test command line for unsafe subcommand " - "and return 0 in case if command is safe and 1 otherwise", nargs="*") + parser.add_argument("subcommand", help="instead of showing commands, just test command line for unsafe subcommand " + "and return 0 in case if command is safe and 1 otherwise", nargs="*") parser.set_defaults(handler=handlers.UnsafeCommands, architecture="", lock=None, quiet=True, report=False, repository="", unsafe=True, parser=_parser) return parser @@ -198,7 +198,7 @@ def _set_help_parser(root: SubParserAction) -> argparse.ArgumentParser: parser = root.add_parser("help", help="show help message", description="show help message for application or command and exit", formatter_class=_formatter) - parser.add_argument("command", help="show help message for specific command", nargs="?") + parser.add_argument("subcommand", help="show help message for specific command", nargs="?") parser.set_defaults(handler=handlers.Help, architecture="", lock=None, quiet=True, report=False, repository="", unsafe=True, parser=_parser) return parser diff --git a/src/ahriman/application/handlers/help.py b/src/ahriman/application/handlers/help.py index 9be37636..89c27882 100644 --- a/src/ahriman/application/handlers/help.py +++ b/src/ahriman/application/handlers/help.py @@ -44,7 +44,7 @@ class Help(Handler): report(bool): force enable or disable reporting """ parser: argparse.ArgumentParser = args.parser() - if args.command is None: + if args.subcommand is None: parser.parse_args(["--help"]) else: - parser.parse_args([args.command, "--help"]) + parser.parse_args([args.subcommand, "--help"]) diff --git a/src/ahriman/application/handlers/unsafe_commands.py b/src/ahriman/application/handlers/unsafe_commands.py index fe87bc47..0cb0eb5e 100644 --- a/src/ahriman/application/handlers/unsafe_commands.py +++ b/src/ahriman/application/handlers/unsafe_commands.py @@ -46,8 +46,8 @@ class UnsafeCommands(Handler): """ parser = args.parser() unsafe_commands = UnsafeCommands.get_unsafe_commands(parser) - if args.command: - UnsafeCommands.check_unsafe(args.command, unsafe_commands, parser) + if args.subcommand: + UnsafeCommands.check_unsafe(args.subcommand, unsafe_commands, parser) else: for command in unsafe_commands: StringPrinter(command)(verbose=True) diff --git a/src/ahriman/web/views/static.py b/src/ahriman/web/views/static.py index 1fd6e7a3..8c422de3 100644 --- a/src/ahriman/web/views/static.py +++ b/src/ahriman/web/views/static.py @@ -17,7 +17,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -from aiohttp.web import HTTPFound +from aiohttp.web import HTTPFound, HTTPNotFound from ahriman.models.user_access import UserAccess from ahriman.web.views.base import BaseView @@ -40,5 +40,8 @@ class StaticView(BaseView): Raises: HTTPFound: on success response + HTTPNotFound: if path is invalid or unknown """ - raise HTTPFound(f"/static{self.request.path}") + if self.request.path in self.ROUTES: # explicit validation + raise HTTPFound(f"/static{self.request.path}") + raise HTTPNotFound diff --git a/tests/ahriman/application/handlers/test_handler_help.py b/tests/ahriman/application/handlers/test_handler_help.py index 6e8f13cd..bb615a0c 100644 --- a/tests/ahriman/application/handlers/test_handler_help.py +++ b/tests/ahriman/application/handlers/test_handler_help.py @@ -18,7 +18,7 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace: argparse.Namespace: generated arguments for these test cases """ args.parser = _parser - args.command = None + args.subcommand = None return args @@ -39,7 +39,7 @@ def test_run_command(args: argparse.Namespace, configuration: Configuration, moc must run command for specific subcommand """ args = _default_args(args) - args.command = "aur-search" + args.subcommand = "aur-search" parse_mock = mocker.patch("argparse.ArgumentParser.parse_args") _, repository_id = configuration.check_loaded() diff --git a/tests/ahriman/application/handlers/test_handler_unsafe_commands.py b/tests/ahriman/application/handlers/test_handler_unsafe_commands.py index f6ca4e8a..dc94a811 100644 --- a/tests/ahriman/application/handlers/test_handler_unsafe_commands.py +++ b/tests/ahriman/application/handlers/test_handler_unsafe_commands.py @@ -19,7 +19,7 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace: argparse.Namespace: generated arguments for these test cases """ args.parser = _parser - args.command = [] + args.subcommand = [] return args @@ -43,7 +43,7 @@ def test_run_check(args: argparse.Namespace, configuration: Configuration, mocke must run command and check if command is unsafe """ args = _default_args(args) - args.command = ["clean"] + args.subcommand = ["clean"] commands_mock = mocker.patch("ahriman.application.handlers.UnsafeCommands.get_unsafe_commands", return_value=["command"]) check_mock = mocker.patch("ahriman.application.handlers.UnsafeCommands.check_unsafe") diff --git a/tests/ahriman/application/test_lock.py b/tests/ahriman/application/test_lock.py index 0375a5b6..646675f4 100644 --- a/tests/ahriman/application/test_lock.py +++ b/tests/ahriman/application/test_lock.py @@ -88,7 +88,7 @@ def test_clear(lock: Lock) -> None: """ must remove lock file """ - lock.path = Path(tempfile.mktemp()) # nosec + lock.path = Path(tempfile.gettempdir()) / "ahriman-test.lock" lock.path.touch() lock.clear() @@ -99,7 +99,7 @@ def test_clear_missing(lock: Lock) -> None: """ must not fail on lock removal if file is missing """ - lock.path = Path(tempfile.mktemp()) # nosec + lock.path = Path(tempfile.gettempdir()) / "ahriman-test.lock" lock.clear() @@ -116,7 +116,7 @@ def test_create(lock: Lock) -> None: """ must create lock """ - lock.path = Path(tempfile.mktemp()) # nosec + lock.path = Path(tempfile.gettempdir()) / "ahriman-test.lock" lock.create() assert lock.path.is_file() @@ -127,7 +127,7 @@ def test_create_exception(lock: Lock) -> None: """ must raise exception if file already exists """ - lock.path = Path(tempfile.mktemp()) # nosec + lock.path = Path(tempfile.gettempdir()) / "ahriman-test.lock" lock.path.touch() with pytest.raises(DuplicateRunError): @@ -149,7 +149,7 @@ def test_create_unsafe(lock: Lock) -> None: must not raise exception if force flag set """ lock.force = True - lock.path = Path(tempfile.mktemp()) # nosec + lock.path = Path(tempfile.gettempdir()) / "ahriman-test.lock" lock.path.touch() lock.create() @@ -161,7 +161,7 @@ def test_watch(lock: Lock, mocker: MockerFixture) -> None: must check if lock file exists """ wait_mock = mocker.patch("ahriman.models.waiter.Waiter.wait") - lock.path = Path(tempfile.mktemp()) # nosec + lock.path = Path(tempfile.gettempdir()) / "ahriman-test.lock" lock.watch() wait_mock.assert_called_once_with(lock.path.is_file) diff --git a/tests/ahriman/web/views/test_view_static.py b/tests/ahriman/web/views/test_view_static.py index 5722e224..8d25e3b6 100644 --- a/tests/ahriman/web/views/test_view_static.py +++ b/tests/ahriman/web/views/test_view_static.py @@ -24,8 +24,19 @@ def test_routes() -> None: async def test_get(client_with_auth: TestClient) -> None: """ - must generate status page correctly (/) + must redirect favicon to static files """ response = await client_with_auth.get("/favicon.ico", allow_redirects=False) assert response.status == 302 assert response.headers["Location"] == "/static/favicon.ico" + + +async def test_get_not_found(client_with_auth: TestClient) -> None: + """ + must raise not found if path is invalid + """ + for route in client_with_auth.app.router.routes(): + if hasattr(route.handler, "ROUTES"): + route.handler.ROUTES = [] + response = await client_with_auth.get("/favicon.ico", allow_redirects=False) + assert response.status == 404