Skip to content

Commit

Permalink
Add warning if scanner path doesn't match its namespace (#43)
Browse files Browse the repository at this point in the history
* Add warning if scanner path doesn't match its namespace

* Make update

* Replace flake8 by ruff

* Update mypy to latest version
  • Loading branch information
mathieu-lemay authored Aug 23, 2023
1 parent ccc03dc commit 6c5dcaa
Show file tree
Hide file tree
Showing 12 changed files with 682 additions and 879 deletions.
33 changes: 0 additions & 33 deletions .flake8

This file was deleted.

17 changes: 15 additions & 2 deletions .makefiles/Makefile.code
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,26 @@ upgrade: cruft.update update

format: ## Run code formatters
format: .phony
poetry run isort .
poetry run ruff --select I --fix .
poetry run black .

lint: ## Run linters
lint: .phony
@echo "Checking if poetry.lock is up-to-date with pyproject.toml"
@poetry lock --check
@echo "Checking for poetry.lock version."
@if ! grep 'lock-version = "2.0"' poetry.lock >/dev/null 2>&1; then \
printf "\e[31mpoetry.lock is using an outdated lock-version. Ensure you have the latest poetry version.\e[0m\n"; \
exit 1; \
fi
@echo "Checking for .rej files."
@if [ `git ls-files '*.rej' | wc -l` -gt 0 ]; then \
printf "\e[31m.rej files found\e[0m\n"; \
exit 1; \
fi

poetry run mypy .
poetry run flake8 .
poetry run ruff .

test: ## Run pytest
test: .phony
Expand Down
18 changes: 15 additions & 3 deletions boostsec/registry_validator/upload_rules_db.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Uploads the Rules DB file."""
import sys
from pathlib import Path
from subprocess import check_call, check_output # noqa: S404
from subprocess import check_call, check_output
from typing import cast
from urllib.parse import urljoin

Expand Down Expand Up @@ -81,15 +81,27 @@ def load_scanners(scanners_path: Path, updated_ns: set[str]) -> list[ScannerName
"""
scanners = []
for module_path in scanners_path.rglob("module.yaml"):
scanner_path = module_path.parent

module_yaml = yaml.safe_load(module_path.read_text())
namespace = module_yaml["namespace"]
if namespace == "default": # Support legacy default scanner name
namespace = "boostsecurityio/native-scanner"

if namespace != str(scanner_path.relative_to(scanners_path)):
print(
"WARNING: Scanner directory "
f'"{scanner_path.relative_to(scanners_path)}" doesn\'t match namespace '
f'"{namespace}". Skipping...'
)
continue

driver = module_yaml["name"]
rules_path = module_path.parent / "rules.yaml"
rules_path = scanner_path / "rules.yaml"
if not rules_path.exists():
print(f'WARNING: rules.yaml not found in "{namespace}". Skipping...')
continue

rules_db_yaml = yaml.safe_load(rules_path.read_text())
rules = RulesDbSchema.parse_obj(rules_db_yaml)
scanners.append(
Expand Down Expand Up @@ -249,7 +261,7 @@ def upload_rules_db(
},
},
)
except Exception as e: # noqa: WPS440
except Exception as e: # noqa: BLE001
_log_error_and_exit(f"Failed to upload rules: {e}.")
else:
if response["setRules"]["__typename"] != "RuleSuccessSchema":
Expand Down
1,313 changes: 506 additions & 807 deletions poetry.lock

Large diffs are not rendered by default.

83 changes: 63 additions & 20 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,12 @@ testing = [

[tool.poetry.group.dev.dependencies]
bandit = "1.7.2" # https://github.com/tylerwince/flake8-bandit/issues/21
black = "^22.3.0"
black = "^23.3.0"
coverage = "^6.2"
flake8 = "^4.0.1"
flake8-bandit = "^2.1.2"
flake8-black = "^0.2.3,!=0.2.5"
flake8-blind-except = "^0.2.0"
flake8-broken-line = "^0.4.0"
flake8-bugbear = "^21.11.29"
flake8-builtins = "^1.5.3"
flake8-comprehensions = "^3.7.0"
flake8-docstrings = "^1.6.0"
flake8-eradicate = "^1.2.0"
flake8-isort = "^4.1.1"
flake8-logging-format = "^0.6.0"
flake8-pytest-style = "^1.5.1"
isort = "^5.10.1"
mypy = "^0.921"
pep8-naming = "^0.12.1"
mypy = "^1.3.0"
pytest = "^7.0.1"
pytest-cov = "^3.0.0"
pytest-cov = "^4.0.0"
ruff = "^0.0.281"
requests-mock = "^1.10.0"
types-jsonschema = "^3.2.0"
types-PyYAML = "^6.0.11"
Expand All @@ -57,9 +43,66 @@ namespace_packages=true
explicit_package_bases=true
strict=true
show_error_codes=true
plugins=[
"pydantic.mypy",
]

[tool.pydantic-mypy]
init_forbid_extra = true
init_typed = true
warn_required_dynamic_aliases = true
warn_untyped_fields = true

[tool.ruff]
exclude = [".venv"]
select = [
"A", # flake8-builtins
#"ARG", # flake8-unused-arguments # TODO: re-enable
"B", # flake8-bugbear
"BLE", # flake8-blind-except
"C4", # flake8-comprehensions
"C90", # mccabe
"D", # pydocstyle
"E", # pycodestyle
"ERA", # flake8-eradicate
"F", # Pyflakes
"G", # flake8-logging-format
"I", # isort
"N", # pep8-naming
"PT", # flake8-pytest-style
"RUF", # ruff
"S", # flake8-bandit
"W", # pycodestyle
]
ignore = [
"B904", # Raise without from inside except
"D104", # Missing docstring in public package
"D203", # 1 blank line required before class docstring (conflicts with D211)
"D213", # Multi-line docstring summary should start at the second line (conflicts with D212)
"G004", # Logging statement uses f-string
"N818", # exception name '...' should be named with an Error suffix
]

[tool.isort]
profile = "black"
[tool.ruff.isort]
known-third-party = ["boostsec"]
known-first-party = ["boostsec.registry_validator"]

[tool.ruff.mccabe]
max-complexity = 7

[tool.ruff.pep8-naming]
classmethod-decorators = [
"classmethod",
"pydantic.validator",
"pydantic.root_validator"
]

[tool.ruff.per-file-ignores]
"tests/*" = [
"S101", # Use of `assert` detected
"S105", # Possible hardcoded password
"S311", # Standard pseudo-random generators are not suitable for cryptographic purposes
]

[tool.pytest.ini_options]
minversion = "6.0"
Expand Down
12 changes: 7 additions & 5 deletions tests/integration/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Conftest."""
import shutil
from pathlib import Path
from subprocess import check_call # noqa: S404
from subprocess import check_call
from typing import Callable

import pytest
Expand All @@ -16,8 +16,9 @@ def registry_path(tmp_path: Path) -> Path:
registry.mkdir(parents=True)

check_call(["git", "init"], cwd=registry) # noqa: S603 S607
check_call( # noqa: S603 S607
["git", "commit", "--allow-empty", "-m", "first commit"], cwd=registry
check_call(
["git", "commit", "--allow-empty", "-m", "first commit"], # noqa: S603 S607
cwd=registry,
)

return registry
Expand All @@ -32,8 +33,9 @@ def commit_changes(registry_path: Path) -> CommitChanges:

def commit() -> None:
check_call(["git", "add", "-A"], cwd=registry_path) # noqa: S603 S607
check_call( # noqa: S603 S607
["git", "commit", "--allow-empty", "-am", "commit"], cwd=registry_path
check_call(
["git", "commit", "--allow-empty", "-am", "commit"], # noqa: S603 S607
cwd=registry_path,
)

return commit
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

api_version: 1.0

id: missing-rules
name: Path and Namespace Mismatch
namespace: something-different-than-the-path

config:
support_diff_scan: true
require_full_repo: false
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import:
- boostsecurityio/mitre-cwe
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
name: Path and Namespace Mismatch
namespace: something-different-than-the-path
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import:
- boostsecurityio/mitre-cwe
48 changes: 48 additions & 0 deletions tests/integration/test_upload_rules_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,3 +518,51 @@ def test_main_module_missing_rules(
assert requests_mock.call_count == 0
assert result.exit_code == 0
assert "WARNING: rules.yaml not found in " in result.stdout


@pytest.mark.parametrize(
"sample",
[
"scanners/others/path-and-namespace-mismatch",
"server-side-scanners/others/path-and-namespace-mismatch",
],
)
def test_main_path_and_namespace_mismatch(
cli_runner: CliRunner,
registry_path: Path,
requests_mock: Mocker,
commit_changes: CommitChanges,
use_sample: UseSample,
sample: str,
) -> None:
"""Should warn if a module's path is not the same as its namespace."""
url = "https://my_endpoint/"
requests_mock.post(
urljoin(url, "/rules-management/graphql"),
json={
"data": {"setRules": {"__typename": "RuleSuccessSchema"}},
},
)

use_sample(sample)
commit_changes()

result = cli_runner.invoke(
app,
[
"--api-endpoint",
url,
"--api-token",
"my-token",
"--registry-path",
str(registry_path),
],
)

assert requests_mock.call_count == 0
assert result.exit_code == 0
assert (
'WARNING: Scanner directory "others/path-and-namespace-mismatch" doesn\'t '
'match namespace "something-different-than-the-path". Skipping...'
in result.stdout
)
21 changes: 12 additions & 9 deletions tests/unit/scanner/test_upload_rules_db.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Test."""
from pathlib import Path
from subprocess import check_call # noqa: S404
from typing import Any
from subprocess import check_call
from typing import Any, Optional
from urllib.parse import urljoin

import pytest
Expand Down Expand Up @@ -36,9 +36,10 @@ def _create_module_and_rules(
rules_db_string: str,
namespace: str = "",
create_rules: bool = True,
module_path: Optional[Path] = None,
) -> Path:
"""Create a module.yaml file."""
modules_path = registry_path / namespace
modules_path = registry_path / (module_path or namespace)
modules_path.mkdir(parents=True)
module_yaml = modules_path / "module.yaml"
module_obj = {
Expand Down Expand Up @@ -72,16 +73,17 @@ def _create_rules_realm(
def _init_repo(git_root: Path) -> None:
"""Initialize an empty git repo."""
check_call(["git", "init"], cwd=git_root) # noqa: S603 S607 noboost
check_call( # noqa: S603 S607 noboost
["git", "commit", "--allow-empty", "-m", "first commit"], cwd=git_root
check_call( # noboost
["git", "commit", "--allow-empty", "-m", "first commit"], # noqa: S603 S607
cwd=git_root,
)


def _commit_all_changes(git_root: Path, message: str = "commit") -> None:
"""Commit all changes in the git_root repo."""
check_call(["git", "add", "-A"], cwd=git_root) # noqa: S603 S607 noboost
check_call( # noqa: S603 S607 noboost
["git", "commit", "-am", message], cwd=git_root
check_call( # noboost
["git", "commit", "-am", message], cwd=git_root # noqa: S603 S607
)


Expand Down Expand Up @@ -128,7 +130,8 @@ def test_load_scanners_default_values(scanners_path: Path) -> None:
_create_module_and_rules(
scanners_path,
yaml.safe_dump(rules_db.dict()),
"default",
namespace="default",
module_path=Path("boostsecurityio/native-scanner"),
)

result = load_scanners(scanners_path, set())
Expand Down Expand Up @@ -264,7 +267,7 @@ def test_get_updated_scanners() -> None:
def test_upload_rules_db(requests_mock: Mocker, with_default: bool) -> None:
"""Test upload_rules_db."""
url = "https://my_endpoint/"
test_token = "my-random-key" # noqa: S105
test_token = "my-random-key"
rules = RuleSchemaFactory.batch(2)
default = None
if with_default:
Expand Down

0 comments on commit 6c5dcaa

Please sign in to comment.