ansible / ansible-lint

ansible-lint checks playbooks for practices and behavior that could potentially be improved and can fix some of the most common ones for you
https://ansible.readthedocs.io/projects/lint/
GNU General Public License v3.0
3.45k stars 654 forks source link

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

Open C0rn3j opened 3 weeks ago

C0rn3j commented 3 weeks ago
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
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 commented 6 days ago

@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 commented 6 days ago

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 ```python [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 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 - https://github.com/ansible/ansible-lint/blob/3b5bee19c2102ea35bc8619795a72c5c73797fc7/src/ansiblelint/file_utils.py#L516-L532

ssbarnea commented 6 days ago

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 commented 6 days ago

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.