diff --git a/.github/workflows/_build-package.yml b/.github/workflows/_build-package.yml index b468257..8c565d0 100644 --- a/.github/workflows/_build-package.yml +++ b/.github/workflows/_build-package.yml @@ -10,11 +10,11 @@ jobs: - name: Set up Python uses: actions/setup-python@v4 with: - python-version: 3.13 - - name: Install build dependencies - run: pip install --no-cache-dir -U pip . build twine + python-version: 3.14 + - name: Install dependencies + run: pip install --no-cache-dir -U pip .['dev'] - name: Build package - run: python -m build --sdist --wheel + run: nox --session=build - name: Upload built distributions uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/_static-checks.yml b/.github/workflows/_static-checks.yml index e31eb1c..dda13eb 100644 --- a/.github/workflows/_static-checks.yml +++ b/.github/workflows/_static-checks.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} @@ -15,10 +15,14 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Install dependencies - run: pip install --no-cache-dir -U pip . black flake8 bandit - - name: Lint check with flake8 - run: flake8 cortexutils/ tests/ setup.py - - name: Format check with black - run: black --check cortexutils/ tests/ setup.py + run: pip install --no-cache-dir -U pip .['dev'] + - name: Style check with ruff + run: nox --session=style + - name: Format check with ruff + run: nox --session=format + - name: Type check with mypy + run: nox --session=type + - name: CVE check with pip-audit + run: nox --session=cve - name: Security check with bandit - run: bandit -r cortexutils/ + run: nox --session=security \ No newline at end of file diff --git a/.github/workflows/_unit-tests.yml b/.github/workflows/_unit-tests.yml index 7b2eed4..5f9f748 100644 --- a/.github/workflows/_unit-tests.yml +++ b/.github/workflows/_unit-tests.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] steps: - uses: actions/checkout@v4 - name: Set up Python @@ -15,6 +15,6 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Install dependencies - run: pip install --no-cache-dir -U pip . + run: pip install --no-cache-dir -U pip .['dev'] - name: Run unit tests - run: python -m unittest --verbose + run: nox --session=test diff --git a/.github/workflows/_upload-package.yml b/.github/workflows/_upload-package.yml index 89073fd..89618d5 100644 --- a/.github/workflows/_upload-package.yml +++ b/.github/workflows/_upload-package.yml @@ -10,14 +10,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Compare tag and package version - run: | - TAG=${GITHUB_REF#refs/*/} - VERSION=$(grep -Po '(?<=version=")[^"]*' setup.py) - if [ "$TAG" != "$VERSION" ]; then - echo "Tag value and package version are different: ${TAG} != ${VERSION}" - exit 1 - fi - name: Download built distributions uses: actions/download-artifact@v4 with: @@ -26,12 +18,12 @@ jobs: - name: Set up Python uses: actions/setup-python@v4 with: - python-version: 3.13 - - name: Install build dependencies - run: pip install --no-cache-dir -U pip . twine + python-version: 3.14 + - name: Install dependencies + run: pip install --no-cache-dir -U pip .['dev'] - name: Upload to PyPI - run: twine upload dist/* + run: nox --session=upload env: TWINE_REPOSITORY_URL: https://upload.pypi.org/legacy/ TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} + TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} \ No newline at end of file diff --git a/cortexutils/analyzer.py b/cortexutils/analyzer.py index 9136224..bad3669 100644 --- a/cortexutils/analyzer.py +++ b/cortexutils/analyzer.py @@ -10,7 +10,6 @@ class Analyzer(Worker): - def __init__(self, job_directory=None, secret_phrases=None): Worker.__init__(self, job_directory, secret_phrases) diff --git a/cortexutils/extractor.py b/cortexutils/extractor.py index d5ef5b0..73440e6 100644 --- a/cortexutils/extractor.py +++ b/cortexutils/extractor.py @@ -11,7 +11,7 @@ class Extractor: """The extractor class tries to detect ioc attribute types using regex-matching. Two functions are provided: - - ``check_string(str)`` which checks a string for a regex matc + - ``check_string(str)`` which checks a string for a regex match and just returns the type - ``check_iterable(itr)`` that iterates over a list or a dictionary and returns a list of {type, value} dicts diff --git a/cortexutils/responder.py b/cortexutils/responder.py index f9ded69..69ebdf0 100644 --- a/cortexutils/responder.py +++ b/cortexutils/responder.py @@ -5,7 +5,6 @@ class Responder(Worker): - def __init__(self, job_directory=None, secret_phrases=None): Worker.__init__(self, job_directory, secret_phrases) @@ -21,7 +20,7 @@ def get_data(self): def report(self, full_report, ensure_ascii=False): """Returns a json dict via stdout. - :param full_report: Responsder results as dict. + :param full_report: Responder results as dict. :param ensure_ascii: Force ascii output. Default: False""" operation_list = [] diff --git a/cortexutils/worker.py b/cortexutils/worker.py index cc2bcba..b4001e3 100644 --- a/cortexutils/worker.py +++ b/cortexutils/worker.py @@ -36,7 +36,7 @@ def __init__(self, job_directory, secret_phrases): if not sys.stdin.isatty(): self._input = json.load(sys.stdin) else: - self.error("Input file doesn" "t exist") + self.error("Input file doesn't exist") # Set parameters self.data_type = self.get_param("dataType", None, "Missing dataType field") @@ -185,7 +185,7 @@ def get_env(self, key, default=None, message=None): def error(self, message, ensure_ascii=False): """Stop analyzer with an error message. - Changing ensure_ascii can be helpful when stucking with ascii <-> utf-8 issues. + Changing ensure_ascii can be helpful when stuck with ascii <-> utf-8 issues. Additionally, the input as returned, too. Maybe helpful when dealing with errors. diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 0000000..8374f16 --- /dev/null +++ b/noxfile.py @@ -0,0 +1,78 @@ +import os + +import nox + +PROJECT_DIR = os.path.dirname(__file__) +CORTEXUTILS_DIR = os.path.join(PROJECT_DIR, "cortexutils/") +TESTS_DIR = os.path.join(PROJECT_DIR, "tests/") + +nox.options.default_venv_backend = "none" +nox.options.keywords = "ci and not test" + + +@nox.session(tags=["ci", "lint"]) +def style(session: nox.Session): + """Run style checks with ruff.""" + session.run("ruff", "check", CORTEXUTILS_DIR, TESTS_DIR, __file__) + + +@nox.session(tags=["ci", "lint"]) +def format(session: nox.Session): + """Run format checks with ruff.""" + session.run("ruff", "format", "--check", CORTEXUTILS_DIR, TESTS_DIR, __file__) + + +@nox.session(tags=["ci", "lint"]) +def type(session: nox.Session): + """Run type checks with mypy.""" + session.run("mypy", "--install-types", "--non-interactive", CORTEXUTILS_DIR) + + +@nox.session(tags=["ci", "audit"]) +def cve(session: nox.Session): + """Run cve checks with pip-audit.""" + session.run("pip-audit", PROJECT_DIR) + + +@nox.session(tags=["ci", "audit"]) +def security(session: nox.Session): + """Run security checks with bandit.""" + session.run("bandit", "-r", CORTEXUTILS_DIR) + + +@nox.session(tags=["ci", "test"]) +def test(session: nox.Session): + """Run unit tests with pytest.""" + + if not session.posargs: + session.run("pytest", "-v", "--cov") + else: + session.run("pytest", *session.posargs) + + +@nox.session(tags=["cd", "build"]) +def build(session: nox.Session): + """Build with the build module.""" + session.run("rm", "-rf", "build/", "dist/") + session.run("python", "-m", "build", "--sdist", "--wheel") + + +@nox.session(tags=["cd", "upload"]) +def upload(session: nox.Session): + """Upload to PyPI using twine.""" + + session.run( + "bash", + "-c", + r""" + TAG=${GITHUB_REF#refs/*/} + VERSION=$(grep -Po '(?<=version = ")[^"]*' pyproject.toml) + if [ "$TAG" != "$VERSION" ]; then + echo "Tag value and package version are different: ${TAG} != ${VERSION}" + exit 1 + else + echo "Matching tag value and package version!" + fi + """, + ) + session.run("twine", "upload", "dist/*") diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..8ed0e73 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,49 @@ +[build-system] +requires = ["setuptools", "setuptools-scm"] +build-backend = "setuptools.build_meta" + +[project] +name = "cortexutils" +version = "2.3.0" +description = "A Python library for including utility classes for Cortex analyzers and responders" +readme = "README" +requires-python = ">=3.10" +dependencies = [] +license = "AGPL-3.0-or-later" +keywords = ["cortex", "analyzer", "responder", "thehive"] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Intended Audience :: Information Technology", + "Intended Audience :: Science/Research", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Topic :: Security", + "Topic :: Software Development :: Libraries :: Python Modules", +] +authors = [{ name = "TheHive-Project", email = "support@thehive-project.org" }] + +[project.urls] +Homepage = "https://github.com/TheHive-Project/cortexutils" + +[project.optional-dependencies] +audit = ["bandit", "pip-audit"] +build = ["build", "twine"] +lint = ["mypy", "ruff", "pre-commit"] +test = ["pytest", "pytest-cov"] +dev = ["cortexutils[audit, lint, test, build]", "nox"] + +[tool.setuptools.packages.find] +include = ["cortexutils*"] + +[tool.coverage.run] +omit = ["tests/*"] + +[tool.pytest.ini_options] +testpaths = ["tests"] \ No newline at end of file diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 8682720..0000000 --- a/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[flake8] -max-line-length=88 \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index 67425db..0000000 --- a/setup.py +++ /dev/null @@ -1,36 +0,0 @@ -from setuptools import setup - -setup( - name="cortexutils", - version="2.2.1", - description=( - "A Python library for including utility classes for " - "Cortex analyzers and responders" - ), - long_description=open("README").read(), - author="TheHive-Project", - author_email="support@thehive-project.org", - license="AGPL-V3", - url="https://github.com/TheHive-Project/Cortex-Analyzers/tree/master/contrib", - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "Intended Audience :: Information Technology", - "Intended Audience :: Science/Research", - "License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)", # noqa - "Natural Language :: English", - "Operating System :: OS Independent", - "Programming Language :: Python", - "Topic :: Security", - "Topic :: Software Development :: Libraries :: Python Modules", - ], - py_modules=[ - "future", - "cortexutils.worker", - "cortexutils.analyzer", - "cortexutils.responder", - "cortexutils.extractor", - ], - install_requires=[], - test_suite="tests", -) diff --git a/tests/test_suite_analyzer.py b/tests/test_suite_analyzer.py index 384ab4c..826a3af 100644 --- a/tests/test_suite_analyzer.py +++ b/tests/test_suite_analyzer.py @@ -26,7 +26,6 @@ def load_test_fixture(fixture_path): class TestMinimalConfig(unittest.TestCase): - def setUp(self): load_test_fixture("fixtures/test-minimal-config.json") self.analyzer = Analyzer() @@ -49,7 +48,6 @@ def test_params_data(self): class TestProxyConfig(unittest.TestCase): - def setUp(self): load_test_fixture("fixtures/test-proxy-config.json") self.analyzer = Analyzer() @@ -65,7 +63,6 @@ def test_proxy_config(self): class TestTlpConfig(unittest.TestCase): - def setUp(self): load_test_fixture("fixtures/test-tlp-config.json") self.analyzer = Analyzer() @@ -97,7 +94,6 @@ def test_check_tlp_ok(self): class TestErrorResponse(unittest.TestCase): - def setUp(self): load_test_fixture("fixtures/test-error-response.json") self.analyzer = Analyzer() @@ -133,7 +129,6 @@ def test_error_response(self): class TestReportResponse(unittest.TestCase): - def setUp(self): load_test_fixture("fixtures/test-report-response.json") self.analyzer = Analyzer() diff --git a/tests/test_suite_extractor.py b/tests/test_suite_extractor.py index d24db39..e85eaba 100644 --- a/tests/test_suite_extractor.py +++ b/tests/test_suite_extractor.py @@ -2,6 +2,7 @@ """ This contains the unit tests for the extractor. """ + import unittest from cortexutils.extractor import Extractor