Bug 1651731: [lint] Only allow files that are typically executable to have shebang lines override permission check; r=linter-reviewers,sylvestre
Differential Revision: https://phabricator.services.mozilla.com/D82949
This commit is contained in:
@@ -52,10 +52,7 @@ config/windows-h-.*.h
|
|||||||
tools/clang-tidy/test/.*
|
tools/clang-tidy/test/.*
|
||||||
|
|
||||||
# We are testing the incorrect formatting.
|
# We are testing the incorrect formatting.
|
||||||
tools/lint/test/files/file-whitespace/
|
tools/lint/test/files/
|
||||||
|
|
||||||
# Test reformatting
|
|
||||||
tools/lint/test/files/clang-format/
|
|
||||||
|
|
||||||
# Contains an XML definition and formatting would break the layout
|
# Contains an XML definition and formatting would break the layout
|
||||||
widget/gtk/MPRISInterfaceDescription.h
|
widget/gtk/MPRISInterfaceDescription.h
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ This linter verifies if a file has unnecessary permissions.
|
|||||||
If a file has execution permissions (+x), file-perm will
|
If a file has execution permissions (+x), file-perm will
|
||||||
generate a warning.
|
generate a warning.
|
||||||
|
|
||||||
It will ignore files starting with ``#!`` (Python or node scripts).
|
It will ignore files starting with ``#!`` for types of files
|
||||||
|
that typically have shebang lines (such as python, node or
|
||||||
|
shell scripts).
|
||||||
|
|
||||||
This linter does not have any affect on Windows.
|
This linter does not have any affect on Windows.
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ file-perm:
|
|||||||
- .cpp
|
- .cpp
|
||||||
- .h
|
- .h
|
||||||
- .html
|
- .html
|
||||||
- .js
|
|
||||||
- .jsm
|
- .jsm
|
||||||
- .jsx
|
- .jsx
|
||||||
- .m
|
- .m
|
||||||
@@ -22,3 +21,15 @@ file-perm:
|
|||||||
- 'tools/lint/file-perm/**'
|
- 'tools/lint/file-perm/**'
|
||||||
type: external
|
type: external
|
||||||
payload: file-perm:lint
|
payload: file-perm:lint
|
||||||
|
|
||||||
|
maybe-shebang-file-perm:
|
||||||
|
description: "File permission check for files that might have `#!` header."
|
||||||
|
include:
|
||||||
|
- .
|
||||||
|
allow-shebang: true
|
||||||
|
extensions:
|
||||||
|
- .js
|
||||||
|
support-files:
|
||||||
|
- 'tools/lint/file-perm/**'
|
||||||
|
type: external
|
||||||
|
payload: file-perm:lint
|
||||||
|
|||||||
@@ -8,10 +8,9 @@ import platform
|
|||||||
from mozlint import result
|
from mozlint import result
|
||||||
from mozlint.pathutils import expand_exclusions
|
from mozlint.pathutils import expand_exclusions
|
||||||
|
|
||||||
results = []
|
|
||||||
|
|
||||||
|
|
||||||
def lint(paths, config, fix=None, **lintargs):
|
def lint(paths, config, fix=None, **lintargs):
|
||||||
|
results = []
|
||||||
|
|
||||||
if platform.system() == "Windows":
|
if platform.system() == "Windows":
|
||||||
# Windows doesn't have permissions in files
|
# Windows doesn't have permissions in files
|
||||||
@@ -21,13 +20,14 @@ def lint(paths, config, fix=None, **lintargs):
|
|||||||
files = list(expand_exclusions(paths, config, lintargs["root"]))
|
files = list(expand_exclusions(paths, config, lintargs["root"]))
|
||||||
for f in files:
|
for f in files:
|
||||||
if os.access(f, os.X_OK):
|
if os.access(f, os.X_OK):
|
||||||
with open(f, "r+") as content:
|
if config.get("allow-shebang"):
|
||||||
# Some source files have +x permissions
|
with open(f, "r+") as content:
|
||||||
line = content.readline()
|
# Some source files have +x permissions
|
||||||
if line.startswith("#!"):
|
line = content.readline()
|
||||||
# Check if the file doesn't start with a shebang
|
if line.startswith("#!"):
|
||||||
# if it does, not a warning
|
# Check if the file doesn't start with a shebang
|
||||||
continue
|
# if it does, not a warning
|
||||||
|
continue
|
||||||
|
|
||||||
if fix:
|
if fix:
|
||||||
# We want to fix it, do it and leave
|
# We want to fix it, do it and leave
|
||||||
|
|||||||
@@ -20,7 +20,39 @@ sys.path.insert(0, lintdir)
|
|||||||
logger = logging.getLogger("mozlint")
|
logger = logging.getLogger("mozlint")
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
def pytest_generate_tests(metafunc):
|
||||||
|
"""Finds, loads and returns the config for the linter name specified by the
|
||||||
|
LINTER global variable in the calling module.
|
||||||
|
|
||||||
|
This implies that each test file (that uses this fixture) should only be
|
||||||
|
used to test a single linter. If no LINTER variable is defined, the test
|
||||||
|
will fail.
|
||||||
|
"""
|
||||||
|
if "config" in metafunc.fixturenames:
|
||||||
|
if not hasattr(metafunc.module, "LINTER"):
|
||||||
|
pytest.fail(
|
||||||
|
"'config' fixture used from a module that didn't set the LINTER variable"
|
||||||
|
)
|
||||||
|
|
||||||
|
name = metafunc.module.LINTER
|
||||||
|
config_path = os.path.join(lintdir, "{}.yml".format(name))
|
||||||
|
parser = Parser(build.topsrcdir)
|
||||||
|
configs = parser.parse(config_path)
|
||||||
|
config_names = {config["name"] for config in configs}
|
||||||
|
|
||||||
|
marker = metafunc.definition.get_closest_marker("lint_config")
|
||||||
|
if marker:
|
||||||
|
config_name = marker.kwargs["name"]
|
||||||
|
if config_name not in config_names:
|
||||||
|
pytest.fail(f"lint config {config_name} not present in {name}.yml")
|
||||||
|
configs = [
|
||||||
|
config for config in configs if config["name"] == marker.kwargs["name"]
|
||||||
|
]
|
||||||
|
|
||||||
|
ids = [config["name"] for config in configs]
|
||||||
|
metafunc.parametrize("config", configs, ids=ids)
|
||||||
|
|
||||||
|
|
||||||
def root(request):
|
def root(request):
|
||||||
"""Return the root directory for the files of the linter under test.
|
"""Return the root directory for the files of the linter under test.
|
||||||
|
|
||||||
@@ -50,27 +82,6 @@ def paths(root):
|
|||||||
return _inner
|
return _inner
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def config(request):
|
|
||||||
"""Finds, loads and returns the config for the linter name specified by the
|
|
||||||
LINTER global variable in the calling module.
|
|
||||||
|
|
||||||
This implies that each test file (that uses this fixture) should only be
|
|
||||||
used to test a single linter. If no LINTER variable is defined, the test
|
|
||||||
will fail.
|
|
||||||
"""
|
|
||||||
if not hasattr(request.module, "LINTER"):
|
|
||||||
pytest.fail(
|
|
||||||
"'config' fixture used from a module that didn't set the LINTER variable"
|
|
||||||
)
|
|
||||||
|
|
||||||
name = request.module.LINTER
|
|
||||||
config_path = os.path.join(lintdir, "{}.yml".format(name))
|
|
||||||
parser = Parser(build.topsrcdir)
|
|
||||||
# TODO Don't assume one linter per yaml file
|
|
||||||
return parser.parse(config_path)[0]
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def run_setup(config):
|
def run_setup(config):
|
||||||
"""Make sure that if the linter named in the LINTER global variable has a
|
"""Make sure that if the linter named in the LINTER global variable has a
|
||||||
@@ -111,7 +122,7 @@ def lint(config, root):
|
|||||||
|
|
||||||
ret = defaultdict(list)
|
ret = defaultdict(list)
|
||||||
for r in results:
|
for r in results:
|
||||||
ret[r.path].append(r)
|
ret[r.relpath].append(r)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|||||||
2
tools/lint/test/files/file-perm/no-shebang/bad-shebang.c
Executable file
2
tools/lint/test/files/file-perm/no-shebang/bad-shebang.c
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
int main() { return 0; }
|
||||||
@@ -1,22 +1,38 @@
|
|||||||
from __future__ import absolute_import, print_function
|
from __future__ import absolute_import, print_function
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
import mozunit
|
import mozunit
|
||||||
|
|
||||||
LINTER = "file-perm"
|
LINTER = "file-perm"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.lint_config(name="file-perm")
|
||||||
def test_lint_file_perm(lint, paths):
|
def test_lint_file_perm(lint, paths):
|
||||||
results = lint(paths())
|
results = lint(paths("no-shebang"), collapse_results=True)
|
||||||
print(results)
|
print(results)
|
||||||
assert len(results) == 2
|
assert len(results) == 2
|
||||||
|
|
||||||
|
assert results.keys() == {
|
||||||
|
"no-shebang/bad.c",
|
||||||
|
"no-shebang/bad-shebang.c",
|
||||||
|
}
|
||||||
|
|
||||||
|
for path, issues in results.items():
|
||||||
|
for issue in issues:
|
||||||
|
assert "permissions on a source" in issue.message
|
||||||
|
assert issue.level == "error"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.lint_config(name="maybe-shebang-file-perm")
|
||||||
|
def test_lint_shebang_file_perm(config, lint, paths):
|
||||||
|
results = lint(paths("maybe-shebang"))
|
||||||
|
print(results)
|
||||||
|
assert len(results) == 1
|
||||||
|
|
||||||
assert "permissions on a source" in results[0].message
|
assert "permissions on a source" in results[0].message
|
||||||
assert results[0].level == "error"
|
assert results[0].level == "error"
|
||||||
assert results[0].relpath == "bad.c"
|
assert results[0].relpath == "maybe-shebang/bad.js"
|
||||||
|
|
||||||
assert "permissions on a source" in results[1].message
|
|
||||||
assert results[1].level == "error"
|
|
||||||
assert results[1].relpath == "bad.js"
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user