Skip to content

v2.12.0

Latest

Choose a tag to compare

@github-actions github-actions released this 12 Aug 10:50

Added

  • SAFE601 missing_assertions now recognises Python assertion method calls, not just the assert keyword. unittest / Django TestCase bodies assert via self.assertEqual(...) / self.assertRaises(...) and pytest via pytest.raises(...) / pytest.warns(...), none of which the rule could previously see - so every unittest-style test read as assertion-less and forced a file-wide SAFE601 ignore (which also hid genuinely under-asserted production code). Python gains a configurable assertion_calls list (bare key, per the Python convention), counted in addition to the assert keyword; call_name strips the receiver so self.assertEqual / pytest.raises match on the bareword. The default set covers the unittest assert* surface plus pytest's raises / warns; extend it with project-specific assertion helpers. This closes the systemic false-positive class surfaced by the optimus-secure-fdn Django review. A mistyped scalar (assertion_calls = "assertEqual") fails loud rather than silently matching characters, matching the other per-language lists.
  • SAFE601 can now be scoped to test functions only via test_functions_only = true (default false, so Holzmann rule 5's production-assertion intent is unchanged for existing configs). When on, the rule fires only for functions named for a test_function_prefixes entry (default ["test"]) that live in a file safelint recognises as a test file (under test_dirs, or matching the language's test-filename convention - the same definition SAFE701/702 use). This lets teams run SAFE601 as a "test must actually assert something" guard: production code that validates by raising, and fixtures / setUp / helper methods that legitimately have no assertions, are skipped, while a test_* function that asserts nothing still fires. The shared test-file identification used by SAFE601/701/702 now lives in one place (rules/_test_files.py).
  • SAFE907 unvalidated_request_input gains a configurable validator allowlist. The rule previously recognised only a hardcoded set of validating calls (Python is_valid / full_clean / validate / model_validate / parse_obj; PHP $request->validate), so a project that validates request input through its own helper (validate_export_request(), an allowlist filter builder) could only silence the rule with a file-level ignore - which then hid any genuinely-unvalidated read elsewhere in that file. New request_validators (Python, bare key) / request_validators_php (PHP) lists are unioned with the built-in set; a read guarded by a listed helper no longer fires. Exact call-name match (receiver stripped), validated via the shared string-list guard so a mistyped scalar fails loud. Also surfaced by the optimus-secure-fdn Django review.

Fixed

  • test_coupling (SAFE702): an explicit file target's changed-files context is now the repo-wide diff, and the rule only fires for a source that actually changed. Two coupled corrections. (1) safelint check foo.py gave foo.py a changed set of just [foo.py], so an overlapping run safelint check pkg/ pkg/foo.py (which dedups to the file target) could false-positive: a sibling test updated in the same commit was dropped from that single-file set, so check pkg/foo.py fired while check pkg/ stayed clean. A file target now carries the full repo-wide diff (falling back to the named file only when git is unavailable), so the verdict is identical however the file is named. (2) test_coupling now gates on the source file being in the changed set, so a named-but-unmodified file (check foo.py on a file you did not touch) no longer trips coupling - it did not change, so there is nothing to couple. Directory and pre-commit (safelint <file> ...) invocations are unaffected: every file they lint is already in the changed set. This supersedes the 2.11.0 "the named file is its own changed set" behaviour for check, which is git-aware (the positional pre-commit hook contract is unchanged).