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

Add: plugin for finding excess space before dot in some tags #751

Merged
merged 9 commits into from
Oct 1, 2024
103 changes: 103 additions & 0 deletions tests/plugins/test_spaces_before_dots.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2024 Greenbone AG

from pathlib import Path

from tests.plugins import PluginTestCase
from troubadix.helper import CURRENT_ENCODING
from troubadix.plugin import LinterFix
from troubadix.plugins.spaces_before_dots import CheckSpacesBeforeDots


class TestSpacesBeforeDots(PluginTestCase):

def test_ok(self):
nasl_file = Path("/some/fake/directory/test.nasl")
content = """
script_tag(name:"summary", value:"Foo Bar.");
script_tag(name:"solution", value:"Foo .NET.");
script_tag(name:"insight", value:"Foo Bar ...");
"""
fake_context = self.create_file_plugin_context(
nasl_file=nasl_file, file_content=content
)
plugin = CheckSpacesBeforeDots(fake_context)
results = list(plugin.run())
self.assertEqual(len(results), 0)

def test_fail(self):
nasl_file = Path("/some/fake/directory/test.nasl")
content = (
"""
script_tag(name:"summary", value:"Foo Bar .");
script_tag(name:"vuldetect", value:"Foo Bar .");
script_tag(name:"insight", value:"Foo Bar .");
script_tag(name:"impact", value:"Foo . Bar . Foo");
"""
'script_tag(name:"affected", value:"Foo\n.\nBar.");'
'script_tag(name:"solution", value:"Foo Bar\n.");'
)
fake_context = self.create_file_plugin_context(
nasl_file=nasl_file, file_content=content
)
plugin = CheckSpacesBeforeDots(fake_context)
results = list(plugin.run())
self.assertEqual(len(results), 7)
self.assertEqual(
results[0].message,
"value of script_tag summary has at least one occurence of excess"
" whitespace before a dot:\n"
" 'script_tag(name:"
'"summary", value:"Foo Bar .");'
"'",
)

def test_ignore(self):
nasl_file = Path("/some/fake/directory/test.inc")
fake_context = self.create_file_plugin_context(nasl_file=nasl_file)
plugin = CheckSpacesBeforeDots(fake_context)

results = list(plugin.run())

self.assertEqual(len(results), 0)

def test_fix(self):
with self.create_directory() as tempdir:
path = tempdir / "file.nasl"
content = (
"""
script_tag(name:"summary", value:"Foo Bar .");
script_tag(name:"vuldetect", value:"Foo Bar .");
script_tag(name:"insight", value:"Foo .Net Bar .");
script_tag(name:"impact", value:"Foo . Bar . Foo");
"""
'script_tag(name:"affected", value:"Foo\n.\nBar.");'
'script_tag(name:"solution", value:"Foo Bar\n.");'
)
expected_modified_content = (
"""
script_tag(name:"summary", value:"Foo Bar.");
script_tag(name:"vuldetect", value:"Foo Bar.");
script_tag(name:"insight", value:"Foo .Net Bar.");
script_tag(name:"impact", value:"Foo. Bar. Foo");
"""
'script_tag(name:"affected", value:"Foo.\nBar.");'
'script_tag(name:"solution", value:"Foo Bar.");'
)
path.write_text(content, encoding=CURRENT_ENCODING)

fake_context = self.create_file_plugin_context(
nasl_file=path, file_content=content
)

plugin = CheckSpacesBeforeDots(fake_context)

# keep list() to consume the iterator
list(plugin.run())

results = list(plugin.fix())
self.assertEqual(len(results), 1)

self.assertIsInstance(results[0], LinterFix)
modified_content = path.read_text(encoding=CURRENT_ENCODING)
self.assertEqual(modified_content, expected_modified_content)
2 changes: 2 additions & 0 deletions troubadix/plugins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from typing import Iterable, List

from troubadix.plugin import FilePlugin, FilesPlugin, Plugin
from troubadix.plugins.spaces_before_dots import CheckSpacesBeforeDots

from .badwords import CheckBadwords
from .copyright_text import CheckCopyrightText
Expand Down Expand Up @@ -140,6 +141,7 @@
CheckVTFilePermissions,
CheckVTPlacement,
CheckWrongSetGetKBCalls,
CheckSpacesBeforeDots,
]

# plugins checking all files
Expand Down
130 changes: 130 additions & 0 deletions troubadix/plugins/spaces_before_dots.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2024 Greenbone AG
import re
from collections.abc import Iterator
from pathlib import Path

from troubadix.helper import CURRENT_ENCODING
from troubadix.helper.helper import is_ignore_file
from troubadix.helper.patterns import (
ScriptTag,
get_script_tag_pattern,
)
from troubadix.plugin import (
FileContentPlugin,
LinterFix,
LinterResult,
LinterWarning,
)

TAGS = [
ScriptTag.SUMMARY,
ScriptTag.VULDETECT,
ScriptTag.INSIGHT,
ScriptTag.IMPACT,
ScriptTag.AFFECTED,
ScriptTag.SOLUTION,
]

# Regex pattern to match:
# 1. A dot preceded and/or followed by any whitespace character (floating between words)
# 2. A dot preceded by any whitespace character at the end of the string
PATTERN = re.compile(r"\s+\.(\s|$)")
IGNORE = [
# 21.04 and 22.04 are generated and should not be touched manually
"21.04/",
"22.04/",
# uses dots for beginning of entry in enumeration
"common/2008/debian/deb_246.nasl",
"common/2008/debian/deb_266.nasl",
"common/2008/freebsd/freebsd_5e92e8a2.nasl",
"common/2008/freebsd/freebsdsa_cpio.nasl",
"common/2008/freebsd/freebsdsa_cvs2.nasl",
"common/2009/osc_photoGallery_sql_injection.nasl",
"common/2009/secpod_novell_edir_mult_vuln_jul09_lin.nasl",
"common/2009/secpod_novell_edir_mult_vuln_jul09_win.nasl",
"common/2010/freebsd/freebsd_3a7c5fc4.nasl",
"common/2012/freebsd/freebsd_a4a809d8.nasl",
"common/2015/amazon/alas-2014-455.nasl",
"common/2015/gb_mozilla_firefox_mult_vuln01_mar15_macosx.nasl",
"common/2015/gb_mozilla_firefox_mult_vuln01_mar15_win.nasl",
"common/2015/oracle/ELSA-2009-1619.nasl",
"common/2015/oracle/ELSA-2011-0586.nasl",
"common/2016/gb_perl_privilege_escalation_vuln_win.nasl",
"common/2021/dropbear/gb_dropbear_ssh_filename_vuln_may20.nasl",
"common/2021/eclipse/gb_jetty_GHSA-v7ff-8wcx-gmc5_lin.nasl",
"common/2021/eclipse/gb_jetty_GHSA-v7ff-8wcx-gmc5_win.nasl",
"common/gsf/2009/mandriva/gb_mandriva_MDVSA_2008_140.nasl",
"common/gsf/2009/mandriva/gb_mandriva_MDVSA_2008_141.nasl",
"common/gsf/2010/mandriva/gb_mandriva_MDVA_2010_173.nasl",
"common/gsf/2010/mandriva/gb_mandriva_MDVSA_2010_155.nasl",
"common/gsf/2010/mandriva/gb_mandriva_MDVSA_2010_155_1.nasl",
"common/gsf/2010/mandriva/gb_mandriva_MDVSA_2010_167.nasl",
"common/gsf/2020/f5/gb_f5_big_ip_K11315080.nasl",
"common/gsf/2020/f5/gb_f5_big_iq_K11315080.nasl",
"common/2022/opensuse/gb_opensuse_2022_1548_1.nasl",
"common/2024/opensuse/gb_opensuse_2023_3247_1.nasl",
"common/attic/debian/deb_232_1.nasl",
]


class CheckSpacesBeforeDots(FileContentPlugin):
name = "check_spaces_before_dots"

def check_content(
self, nasl_file: Path, file_content: str
) -> Iterator[LinterResult]:
"""
This plugin checks for excess whitespace before a dot
in script_tags that have full sentence values
"""
self.matches = []
if nasl_file.suffix == ".inc" or is_ignore_file(nasl_file, IGNORE):
return
for tag in TAGS:
pattern = get_script_tag_pattern(tag)
match = pattern.search(file_content)
if not match:
continue

value = match.group("value")
value_start = match.start("value")

for excess_match in PATTERN.finditer(value):
whitespace_pos = excess_match.start() + value_start
self.matches.append((whitespace_pos, excess_match.group()))
fullmatch = match.group()
yield LinterWarning(
f"value of script_tag {match.group('name')} has at least"
" one occurence of excess whitespace before a dot:"
f"\n '{fullmatch}'",
file=nasl_file,
plugin=self.name,
)

def fix(self) -> Iterator[LinterResult]:

if not self.matches:
return

Check warning on line 108 in troubadix/plugins/spaces_before_dots.py

View check run for this annotation

Codecov / codecov/patch

troubadix/plugins/spaces_before_dots.py#L108

Added line #L108 was not covered by tests

# Sort matches by position, descending order to avoid messing up indices during replacement
self.matches.sort(reverse=True)

file_content = self.context.file_content
for pos, match_str in self.matches:
# Replace the match by removing the excess whitespace before the dot
fixed_str = re.sub(r"\s+\.", ".", match_str)
file_content = (
file_content[:pos]
+ fixed_str
+ file_content[pos + len(match_str) :]
)

with open(self.context.nasl_file, "w", encoding=CURRENT_ENCODING) as f:
f.write(file_content)

yield LinterFix(
"Excess spaces were removed",
file=self.context.nasl_file,
plugin=self.name,
)
Loading