Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Permission denied: '/.config/ansible-lint.yml' when no config file exists and executed outside of a project #4306

Open
C0rn3j opened this issue Aug 30, 2024 · 5 comments
Labels

Comments

@C0rn3j
Copy link

C0rn3j commented Aug 30, 2024

Summary

I have originally reported this issue downstream in the Ansible VSC extension, but it seems it's an issue in ansible-lint.

Issue Type
  • Bug Report
OS / ENVIRONMENT
[130] % rm -f ~/.config/ansible-lint.yml; ansible-lint --version
Traceback (most recent call last):
  File "/usr/bin/ansible-lint", line 8, in <module>
    sys.exit(_run_cli_entrypoint())
             ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/ansiblelint/__main__.py", line 408, in _run_cli_entrypoint
    sys.exit(main(sys.argv))
             ^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/ansiblelint/__main__.py", line 290, in main
    cache_dir_lock = initialize_options(argv[1:])
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/ansiblelint/__main__.py", line 125, in initialize_options
    new_options = cli.get_config(arguments or [])
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/ansiblelint/cli.py", line 606, in get_config
    project_dir, method = find_project_root(
                          ^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/ansiblelint/file_utils.py", line 526, in find_project_root
    if resolved_cfg_path.is_file():
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/pathlib.py", line 892, in is_file
    return S_ISREG(self.stat().st_mode)
                   ^^^^^^^^^^^
  File "/usr/lib/python3.12/pathlib.py", line 840, in stat
    return os.stat(self, follow_symlinks=follow_symlinks)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
PermissionError: [Errno 13] Permission denied: '/.config/ansible-lint.yml'
[0] % echo '{}' > ~/.config/ansible-lint.yml 

[0] % ansible-lint --version                
ansible-lint 24.7.1.dev0 using ansible-core:2.17.3 ansible-compat:24.8.0 ruamel-yaml:0.18.6 ruamel-yaml-clib:0.2.8

Running on Arch Linux from the repo packages:

% pacman -Q ansible-core ansible-lint
ansible-core 2.17.3-1
ansible-lint 24.7.0-1
STEPS TO REPRODUCE

Execute ansible-lint in home folder. even --version fails.

Desired Behavior

No error.

Actual Behavior

Error.

@ssbarnea
Copy link
Member

@C0rn3j You failed to mention a critical piece of information: what is the current directory in which you did run the linter.

If you run linter at / and you are not root and /.config/ansible-lint.yml exists and is owned by root, I do expect to see such an error. We do all agree that the stacktrace is a bug, but still a failure would be expected.

Still, I suspect that you face this in another case and I am quite curious to find a way to reproduce it, so we can fix it well.

@C0rn3j
Copy link
Author

C0rn3j commented Sep 20, 2024

You failed to mention a critical piece of information: what is the current directory in which you did run the linter.

Home of my user.

Now that you mention that, I can see I easily repro this by simply adding the hack, verifying it works them cd'ing from $HOME to /tmp and it breaks again.

irrelevant now
[130] % ansible-lint --version
ansible-lint 24.9.3.dev0 using ansible-core:2.17.4 ansible-compat:24.9.1 ruamel-yaml:0.18.6 ruamel-yaml-clib:0.2.8

[0] % cd /tmp

[0] % ansible-lint --version
Traceback (most recent call last):
  File "/usr/bin/ansible-lint", line 8, in <module>
    sys.exit(_run_cli_entrypoint())
             ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/ansiblelint/__main__.py", line 411, in _run_cli_entrypoint
    sys.exit(main(sys.argv))
             ^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/ansiblelint/__main__.py", line 290, in main
    cache_dir_lock = initialize_options(argv[1:])
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/ansiblelint/__main__.py", line 125, in initialize_options
    new_options = cli.get_config(arguments or [])
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/ansiblelint/cli.py", line 606, in get_config
    project_dir, method = find_project_root(
                          ^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/ansiblelint/file_utils.py", line 526, in find_project_root
    if resolved_cfg_path.is_file():
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/pathlib.py", line 892, in is_file
    return S_ISREG(self.stat().st_mode)
                   ^^^^^^^^^^^
  File "/usr/lib/python3.12/pathlib.py", line 840, in stat
    return os.stat(self, follow_symlinks=follow_symlinks)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
PermissionError: [Errno 13] Permission denied: '/.config/ansible-lint.yml'

EDIT 1:

It seems to simply resolve upwards all the way til it hits / and tries to read a file it does not have permissions for?

# cwd /usr/lib/python3.12/site-packages/ansiblelint
[0] % ansible-lint --version
# find_project_root() in file_utils.py
directory: /usr/lib/python3.12/site-packages/ansiblelint; of type <class 'pathlib.PosixPath'>
  resolved_cfg_path: /usr/lib/python3.12/site-packages/ansiblelint; of type <class 'pathlib.PosixPath'> / cfg_file: .ansible-lint of type <class 'str'>
  resolved_cfg_path: /usr/lib/python3.12/site-packages/ansiblelint; of type <class 'pathlib.PosixPath'> / cfg_file: .config/ansible-lint.yml of type <class 'str'>
  resolved_cfg_path: /usr/lib/python3.12/site-packages/ansiblelint; of type <class 'pathlib.PosixPath'> / cfg_file: .config/ansible-lint.yaml of type <class 'str'>
directory: /usr/lib/python3.12/site-packages; of type <class 'pathlib.PosixPath'>
  resolved_cfg_path: /usr/lib/python3.12/site-packages; of type <class 'pathlib.PosixPath'> / cfg_file: .ansible-lint of type <class 'str'>
  resolved_cfg_path: /usr/lib/python3.12/site-packages; of type <class 'pathlib.PosixPath'> / cfg_file: .config/ansible-lint.yml of type <class 'str'>
  resolved_cfg_path: /usr/lib/python3.12/site-packages; of type <class 'pathlib.PosixPath'> / cfg_file: .config/ansible-lint.yaml of type <class 'str'>
directory: /usr/lib/python3.12; of type <class 'pathlib.PosixPath'>
  resolved_cfg_path: /usr/lib/python3.12; of type <class 'pathlib.PosixPath'> / cfg_file: .ansible-lint of type <class 'str'>
  resolved_cfg_path: /usr/lib/python3.12; of type <class 'pathlib.PosixPath'> / cfg_file: .config/ansible-lint.yml of type <class 'str'>
  resolved_cfg_path: /usr/lib/python3.12; of type <class 'pathlib.PosixPath'> / cfg_file: .config/ansible-lint.yaml of type <class 'str'>
directory: /usr/lib; of type <class 'pathlib.PosixPath'>
  resolved_cfg_path: /usr/lib; of type <class 'pathlib.PosixPath'> / cfg_file: .ansible-lint of type <class 'str'>
  resolved_cfg_path: /usr/lib; of type <class 'pathlib.PosixPath'> / cfg_file: .config/ansible-lint.yml of type <class 'str'>
  resolved_cfg_path: /usr/lib; of type <class 'pathlib.PosixPath'> / cfg_file: .config/ansible-lint.yaml of type <class 'str'>
directory: /usr; of type <class 'pathlib.PosixPath'>
  resolved_cfg_path: /usr; of type <class 'pathlib.PosixPath'> / cfg_file: .ansible-lint of type <class 'str'>
  resolved_cfg_path: /usr; of type <class 'pathlib.PosixPath'> / cfg_file: .config/ansible-lint.yml of type <class 'str'>
  resolved_cfg_path: /usr; of type <class 'pathlib.PosixPath'> / cfg_file: .config/ansible-lint.yaml of type <class 'str'>
directory: /; of type <class 'pathlib.PosixPath'>
  resolved_cfg_path: /; of type <class 'pathlib.PosixPath'> / cfg_file: .ansible-lint of type <class 'str'>
  resolved_cfg_path: /; of type <class 'pathlib.PosixPath'> / cfg_file: .config/ansible-lint.yml of type <class 'str'>
Traceback (most recent call last):
...
PermissionError: [Errno 13] Permission denied: '/.config/ansible-lint.yml'

EDIT: Here's the problem, I have a legitimate /.config directory somehow. It sees that and tries to read it despite not having any permissions for it, and crashes.

drwxr-x--- root   root   4.0 KB Mon Nov 14 20:12:17 2022  .config

[0] # tree ./
.
└── lxc
    └── config.yml

2 directories, 1 file

EDIT2:

    for directory in (common_base, *common_base.parents):
 #       print(f'directory: {directory}; of type {type(directory)}')
        if directory is None:
            exit(125)
        if (directory / ".git").exists():
            return directory, ".git directory"

        if (directory / ".hg").is_dir():
            return directory, ".hg directory"

        for cfg_file in cfg_files:
            # note that if cfg_file is already absolute, 'directory' is ignored
            resolved_cfg_path = directory / cfg_file
#            print(f'resolved_cfg_path: {directory}; of type {type(directory)} / cfg_file: {cfg_file} of type {type(cfg_file)}')
            try:
                if resolved_cfg_path.is_file():
                    if os.path.isabs(cfg_file):
                        directory = Path(cfg_file).parent
                        if directory.name == ".config":
                            directory = directory.parent
                    return directory, f"config file {resolved_cfg_path}"
            except PermissionError:
                print(f"Permission error trying to read {resolved_cfg_path}")

aand third email is the charm, this nets me:

[0] % ansible-lint --version 
Permission error trying to read /.config/ansible-lint.yml
Permission error trying to read /.config/ansible-lint.yaml
ansible-lint 24.9.3.dev0 using ansible-core:2.17.4 ansible-compat:24.9.1 ruamel-yaml:0.18.6 ruamel-yaml-clib:0.2.8

Unsure what's the proper fix, but catching exceptions at least allows the code to run with warnings.

Code here -

for directory in (common_base, *common_base.parents):
if (directory / ".git").exists():
return directory, ".git directory"
if (directory / ".hg").is_dir():
return directory, ".hg directory"
for cfg_file in cfg_files:
# note that if cfg_file is already absolute, 'directory' is ignored
resolved_cfg_path = directory / cfg_file
if resolved_cfg_path.is_file():
if os.path.isabs(cfg_file):
directory = Path(cfg_file).parent
if directory.name == ".config":
directory = directory.parent
return directory, f"config file {resolved_cfg_path}"

@ssbarnea
Copy link
Member

I am afraid that we will not want to ignore a permission denied error, so a fix would only remove the stack trace without allowing the linter to run. -- that is for genuine reasons, as we do not want to give false positives when linter is unable to access files.

@C0rn3j
Copy link
Author

C0rn3j commented Sep 20, 2024

I am afraid that we will not want to ignore a permission denied error, so a fix would only remove the stack trace without allowing the linter to run. -- that is for genuine reasons, as we do not want to give false positives when linter is unable to access files.

I do not think it should be removed, but it should indeed be handled and logged at minimum.

As long as the user won't need to spend an hour debugging to figure out why it randomly crashes, I think it's a good thing.

Am not sure if I even need the /.config folder, it probably got created on accident.

@cavcrosby
Copy link
Contributor

Continuing to move this along, I think it would be worth at least covering the stack trace with a more digestible message. I like your suggestions @C0rn3j, but I would probably use something like _logger.info("Permission denied: %s" % resolved_cfg_path) instead of print(f"Permission error trying to read {resolved_cfg_path}").

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: No status
Development

No branches or pull requests

4 participants