fix: fix some security issues

This commit is contained in:
Evgenii Alekseev 2023-11-12 22:22:05 +02:00
parent 62dd77317d
commit e03fcbfab5
11 changed files with 47 additions and 33 deletions

View File

@ -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')

View File

@ -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

View File

@ -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=(

View File

@ -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

View File

@ -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"])

View File

@ -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)

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
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

View File

@ -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()

View File

@ -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")

View File

@ -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)

View File

@ -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