Skip to content

Commit

Permalink
Merge branch 'main' into feature/#233-add-nox-task-to-verify-dependen…
Browse files Browse the repository at this point in the history
…cy-declarations
  • Loading branch information
Jannis-Mittenzwei committed Sep 25, 2024
2 parents 836ab35 + c922bc2 commit 5414dd7
Show file tree
Hide file tree
Showing 9 changed files with 213 additions and 6 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,34 @@ jobs:
- name: Run type-check
run: poetry run nox -s type-check

security-job:
name: Security Checking (Python-${{ matrix.python-version }})
needs: [ version-check-job ]
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: [ "3.8", "3.9", "3.10", "3.11" ]

steps:
- name: SCM Checkout
uses: actions/checkout@v4

- name: Setup Python & Poetry Environment
uses: ./.github/actions/python-environment
with:
python-version: ${{ matrix.python-version }}

- name: Run security
run: poetry run nox -s security

- name: Upload Artifacts
uses: actions/[email protected]
with:
name: security-python${{ matrix.python-version }}
path: .security.json
include-hidden-files: true

tests-job:
name: Tests (Python-${{ matrix.python-version }}, Exasol-${{ matrix.exasol-version}})
needs: [ build-documentation-job, lint-job, type-check-job ]
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ jobs:
run: |
cp coverage-python3.9/.coverage ../
cp lint-python3.9/.lint.txt ../
cp security-python3.9/.security.json ../
- name: Generate Report
run: poetry run nox -s report -- -- --format json | tee metrics.json
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.lint.json
.lint.txt
.security.json

odbcconfig/odbcinst.ini

Expand Down
47 changes: 44 additions & 3 deletions exasol/toolbox/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
Any,
Callable,
Dict,
List,
Optional,
Union,
)
Expand Down Expand Up @@ -67,6 +68,26 @@ def from_score(score: float) -> "Rating":
"Uncategorized score, score should be in the following interval [0,10]."
)

@staticmethod
def bandit_rating(score: float) -> "Rating":
score = round(score, 3)
if score <= 0.2:
return Rating.F
elif 0.2 < score <= 1.6:
return Rating.E
elif 1.6 < score <= 3:
return Rating.D
elif 3 < score <= 4.4:
return Rating.C
elif 4.4 < score <= 5.8:
return Rating.B
elif 5.8 < score <= 6:
return Rating.A
else:
raise ValueError(
"Uncategorized score, score should be in the following interval [0,6]."
)


@dataclass(frozen=True)
class Report:
Expand Down Expand Up @@ -124,8 +145,27 @@ def reliability() -> Rating:
return Rating.NotAvailable


def security() -> Rating:
return Rating.NotAvailable
def security(file: Union[str, Path]) -> Rating:
with open(file) as json_file:
security_lint = json.load(json_file)
return Rating.bandit_rating(_bandit_scoring(security_lint["results"]))


def _bandit_scoring(ratings: List[Dict[str, Any]]) -> float:
def char(value: str, default: str = "H") -> str:
if value in ["HIGH", "MEDIUM", "LOW"]:
return value[0]
return default

weight = {"LL": 1/18, "LM": 1/15, "LH": 1/12, "ML": 1/9, "MM": 1/6, "MH": 1/3}
exp = 0.0
for infos in ratings:
severity = infos["issue_severity"]
if severity == "HIGH":
return 0.0
index = char(severity) + char(infos["issue_confidence"])
exp += weight[index]
return 6 * (2**-exp)


def technical_debt() -> Rating:
Expand All @@ -137,14 +177,15 @@ def create_report(
date: Optional[datetime.datetime] = None,
coverage_report: Union[str, Path] = ".coverage",
pylint_report: Union[str, Path] = ".lint.txt",
bandit_report: Union[str, Path] = ".security.json",
) -> Report:
return Report(
commit=commit,
date=date if date is not None else datetime.datetime.now(),
coverage=total_coverage(coverage_report),
maintainability=maintainability(pylint_report),
reliability=reliability(),
security=security(),
security=security(bandit_report),
technical_debt=technical_debt(),
)

Expand Down
34 changes: 34 additions & 0 deletions exasol/toolbox/nox/_lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,33 @@ def _type_check(session: Session, files: Iterable[str]) -> None:
)


def _security_lint(session: Session, files: Iterable[str]) -> None:
session.run(
"poetry",
"run",
"bandit",
"--severity-level",
"low",
"--quiet",
"--format",
"json",
"--output",
".security.json",
"--exit-zero",
*files,
)
session.run(
"poetry",
"run",
"bandit",
"--severity-level",
"low",
"--quiet",
"--exit-zero",
*files,
)


@nox.session(python=False)
def lint(session: Session) -> None:
"""Runs the linter on the project"""
Expand All @@ -49,3 +76,10 @@ def type_check(session: Session) -> None:
"""Runs the type checker on the project"""
py_files = [f"{file}" for file in python_files(PROJECT_CONFIG.root)]
_type_check(session, py_files)


@nox.session(name="security", python=False)
def security_lint(session: Session) -> None:
"""Runs the security linter on the project"""
py_files = [f"{file}" for file in python_files(PROJECT_CONFIG.root)]
_security_lint(session, list(filter(lambda file: "test" not in file, py_files)))
3 changes: 2 additions & 1 deletion exasol/toolbox/nox/_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,11 @@ def report(session: Session) -> None:
required_files = (
PROJECT_CONFIG.root / ".coverage",
PROJECT_CONFIG.root / ".lint.txt",
PROJECT_CONFIG.root / ".security.json",
)
if not all(file.exists() for file in required_files):
session.error(
"Please make sure you run the `coverage` and the `lint` target first"
"Please make sure you run the `coverage`, `security` and the `lint` target first"
)
sha1 = str(
session.run("git", "rev-parse", "HEAD", external=True, silent=True)
Expand Down
54 changes: 52 additions & 2 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ sphinx-design = ">=0.5.0,<1"
typer = {extras = ["all"], version = ">=0.7.0"}


bandit = {extras = ["toml"], version = "^1.7.9"}
[tool.poetry.group.dev.dependencies]
autoimport = "^1.4.0"

Expand Down
50 changes: 50 additions & 0 deletions test/unit/report_test.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
from inspect import cleandoc
from typing import (
Dict,
List,
)

import pytest

from exasol.toolbox.metrics import (
Rating,
_bandit_scoring,
_static_code_analysis,
)

Expand Down Expand Up @@ -110,3 +115,48 @@ def test_static_code_analysis(
coverage_report = named_temp_file(name=".lint.txt", content=content)
actual = _static_code_analysis(coverage_report)
assert actual == expected


def _level(char):
levels = {"H": "HIGH", "M": "MEDIUM", "L": "LOW"}
return levels[char]


def _ratings(cases):
output = []
for rating in cases:
output.append(
{
"issue_severity": _level(rating[0]),
"issue_confidence": _level(rating[1]),
}
)
return output


@pytest.mark.parametrize(
"given,expected",
[
(["HH", "LL"], 0),
(["HM", "LM", "ML"], 0),
(["HL", "MH"], 0),
([], 6),
],
)
def test_bandit_value(given, expected):
assert _bandit_scoring(_ratings(given)) == expected


@pytest.mark.parametrize(
"lower,higher",
[
(["HL"], ["MH"]),
(["MH"], ["MM"]),
(["MM"], ["ML"]),
(["HL"], ["LL"]),
(["LL"], []),
(["MH", "LL"], ["MH"]),
],
)
def test_bandit_order(lower, higher):
assert _bandit_scoring(_ratings(lower)) < _bandit_scoring(_ratings(higher))

0 comments on commit 5414dd7

Please sign in to comment.