Compare commits

...

2 Commits

Author SHA1 Message Date
Carlos Polop
1473fedcbf Fix linPEAS module section path matching 2026-01-21 15:21:50 +01:00
Carlos Polop
f8f4250b81 Add stronger winPEAS/linPEAS tests 2026-01-21 15:14:08 +01:00
5 changed files with 101 additions and 2 deletions

View File

@@ -8,6 +8,7 @@ from .yamlGlobals import (
class LinpeasModule:
def __init__(self, path):
self.path = path
real_path = os.path.realpath(path)
with open(path, 'r') as file:
self.module_text = file.read()
@@ -29,7 +30,7 @@ class LinpeasModule:
self.section_info = {}
if not (self.is_base or self.is_function or self.is_variable):
for module in LINPEAS_PARTS["modules"]:
if module["folder_path"] in path:
if os.path.realpath(module["folder_path"]) in real_path:
self.section_info = module
self.is_check = True
break

View File

@@ -0,0 +1,60 @@
import re
import sys
import unittest
from pathlib import Path
class LinpeasModulesMetadataTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.repo_root = Path(__file__).resolve().parents[2]
cls.linpeas_dir = cls.repo_root / "linPEAS"
cls.parts_dir = cls.linpeas_dir / "builder" / "linpeas_parts"
# Ensure `import builder.*` works when tests are run from repo root.
sys.path.insert(0, str(cls.linpeas_dir))
from builder.src.linpeasModule import LinpeasModule # pylint: disable=import-error
cls.LinpeasModule = LinpeasModule
def _iter_module_files(self):
return sorted(self.parts_dir.rglob("*.sh"))
def test_all_modules_parse(self):
module_files = self._iter_module_files()
self.assertGreater(len(module_files), 0, "No linPEAS module files were found.")
# Parsing a module validates its metadata and dependencies.
for path in module_files:
_ = self.LinpeasModule(str(path))
def test_check_module_id_matches_filename(self):
for path in self._iter_module_files():
module = self.LinpeasModule(str(path))
if not getattr(module, "is_check", False):
continue
# For checks, the filename (without numeric prefix) must match the module ID
# (either full ID or stripping section prefix like `SI_`).
file_base = re.sub(r"^[0-9]+_", "", path.stem)
module_id = getattr(module, "id", "")
module_id_tail = module_id[3:] if len(module_id) >= 3 else ""
self.assertIn(
file_base,
{module_id, module_id_tail},
f"Module ID mismatch in {path}: id={module_id} expected suffix={file_base}",
)
def test_module_ids_are_unique(self):
ids = []
for path in self._iter_module_files():
module = self.LinpeasModule(str(path))
ids.append(getattr(module, "id", ""))
duplicates = {x for x in ids if x and ids.count(x) > 1}
self.assertEqual(set(), duplicates, f"Duplicate module IDs found: {sorted(duplicates)}")
if __name__ == "__main__":
unittest.main()

View File

@@ -29,6 +29,7 @@ namespace winPEAS.Tests
Assert.IsFalse(InvokeIsNetworkTypeValid("-network="));
Assert.IsFalse(InvokeIsNetworkTypeValid("-network=10.10.10.999"));
Assert.IsFalse(InvokeIsNetworkTypeValid("-network=10.10.10.10/64"));
Assert.IsFalse(InvokeIsNetworkTypeValid("-network=999.999.999.999/24"));
Assert.IsFalse(InvokeIsNetworkTypeValid("-network=not-an-ip"));
}
}

View File

@@ -0,0 +1,37 @@
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace winPEAS.Tests
{
[TestClass]
public class ChecksArgumentEdgeCasesTests
{
[TestMethod]
public void ShouldNotThrowOnEmptyLogFileArg()
{
// Should return early with a user-friendly error, not crash.
Program.Main(new[] { "log=" });
}
[TestMethod]
public void ShouldNotThrowOnPortsWithoutNetwork()
{
// Should warn and return early because -network was not provided.
Program.Main(new[] { "-ports=80,443" });
}
[TestMethod]
public void ShouldNotThrowOnInvalidNetworkArgument()
{
// Should warn and return early because the IP is invalid.
Program.Main(new[] { "-network=10.10.10.999" });
}
[TestMethod]
public void ShouldNotThrowOnEmptyNetworkArgument()
{
// Should warn and return early because the value is empty.
Program.Main(new[] { "-network=" });
}
}
}

View File

@@ -356,7 +356,7 @@ namespace winPEAS.Checks
{
var rangeParts = networkType.Split('/');
if (rangeParts.Length == 2 && int.TryParse(rangeParts[1], out int res) && res <= 32 && res >= 0)
if (rangeParts.Length == 2 && IPAddress.TryParse(rangeParts[0], out _) && int.TryParse(rangeParts[1], out int res) && res <= 32 && res >= 0)
{
return true;
}