Download tests/test_layers.py from DBax127/nexa: direct link, hf CLI and curl.
- Browser
- Download file 30.7 kB
-
https://huggingface.co/DBax127/nexa/resolve/main/tests/test_layers.py
- Command line
-
hf download hf://DBax127/nexa/tests/test_layers.py
-
curl -L -o test_layers.py https://huggingface.co/DBax127/nexa/resolve/main/tests/test_layers.py
30.7 kB
| """Layer verdicts, and the three false passes they used to hand out. | |
| A false pass is the worst output this project can produce: it is the tool | |
| asserting something it never established. All three tests below fail against the | |
| code as it stood before they were written. | |
| """ | |
| import pytest | |
| import layers | |
| from fixtures import answers | |
| # --------------------------------------------------- enforcement-layer honesty | |
| def test_database_claim_over_orm_constraint_is_caught(): | |
| """The 30B's best answer of the session, and its one false statement. | |
| It declared 'database constraint enforced by PostgreSQL' directly above an | |
| @api.constrains method. That is a Python constraint -- raw SQL walks past | |
| it, which is precisely what the sentence denied. Every structural check | |
| passed, because the label was present. The run was PASS. | |
| """ | |
| message = layers._enforcement_contradiction(answers.CROSS_TABLE_FALSE_CLAIM) | |
| assert message is not None | |
| assert "@api.constrains" in message | |
| assert "_sql_constraints" in message | |
| def test_same_code_with_an_honest_label_is_silent(): | |
| """The pair that makes the check meaningful. | |
| Identical code to the test above. Only the label differs, so anything firing | |
| on both is reacting to the code and not to the claim. | |
| """ | |
| assert layers._enforcement_contradiction(answers.CROSS_TABLE_HONEST) is None | |
| def test_naming_raw_sql_as_the_bypass_is_not_a_database_claim(): | |
| """The format asks what bypasses the rule, so 'bypassed by raw SQL' is the | |
| correct answer to a question that was asked -- not a claim of SQL | |
| enforcement. Reading the whole line instead of its head would punish the | |
| most honest answers hardest.""" | |
| assert layers._enforcement_contradiction(answers.HONEST_BYPASS_MENTION) is None | |
| def test_database_claim_with_sql_constraints_present_is_silent(): | |
| assert layers._enforcement_contradiction(answers.SQL_CONSTRAINT) is None | |
| def test_no_enforcement_label_is_not_a_contradiction(): | |
| """A missing label is already reported by the label check. Reporting it | |
| twice, under a different name, is noise.""" | |
| assert layers._enforcement_contradiction(answers.UNLABELLED_REFUSAL) is None | |
| def test_structural_layer_reports_the_contradiction_as_a_finding(): | |
| result = layers.layer_structural(answers.CROSS_TABLE_FALSE_CLAIM) | |
| assert result.verdict == layers.WARN | |
| assert any("database-level enforcement" in f.message for f in result.findings) | |
| assert "the declared enforcement layer matches the code" in result.checked | |
| def test_structural_layer_passes_the_honest_twin(): | |
| assert layers.layer_structural(answers.CROSS_TABLE_HONEST).verdict == layers.PASS | |
| def test_missing_labels_are_reported_individually(): | |
| result = layers.layer_structural(answers.UNLABELLED_REFUSAL) | |
| missing = [f.message for f in result.findings] | |
| assert any("Enforcement layer:" in m for m in missing) | |
| assert any("Not handled:" in m for m in missing) | |
| # ------------------------------------------------------ what counts as a citation | |
| BULLETED_IDS = {"python.mutable-default-argument", | |
| "python.subprocess-shell-true-is-injection", | |
| "python.assert-is-not-validation", | |
| "python.bare-except-catches-the-exit"} | |
| def test_a_list_under_a_bare_label_is_the_claim(): | |
| """The 7B's own answer. Read from the label's line alone it cited nothing, | |
| and the knowledge layer said so about an answer naming four rules.""" | |
| assert layers.cited_ids(answers.LOAD_PRICES_BULLETED) == BULLETED_IDS | |
| def test_a_claim_on_the_line_reads_as_it_always_did(): | |
| assert layers.cited_ids(answers.CROSS_TABLE_HONEST) == { | |
| "odoo.check-constraint-cannot-cross-tables", | |
| "odoo.constrains-must-list-every-field-read"} | |
| def test_the_list_ends_where_the_next_section_begins(): | |
| """A rule named under 'Not handled:' is the opposite of a claim to have | |
| applied it, and the Enforcement layer list above the label is not one | |
| either.""" | |
| text = answers.LOAD_PRICES_BULLETED.replace( | |
| "- The code does not involve any ORM or SQL constraints, so no other rules apply.", | |
| "- python.eval-and-pickle-execute-their-input, which nothing here calls") | |
| assert layers.cited_ids(text) == BULLETED_IDS | |
| def test_an_invented_id_in_a_list_is_still_a_fabrication(odoo_rules): | |
| """The false pass this closes. On the line, a rule that does not exist is a | |
| critical finding. In a list, the layer never looked, and returned UNKNOWN.""" | |
| text = answers.LOAD_PRICES_BULLETED.replace( | |
| "Rules applied:\n- python.mutable-default-argument", | |
| "Rules applied:\n- python.this-rule-does-not-exist") | |
| result = layers.layer_knowledge(text, odoo_rules) | |
| assert result.verdict == layers.FAIL | |
| assert any("python.this-rule-does-not-exist" in f.message for f in result.findings) | |
| def test_the_knowledge_layer_checks_a_listed_claim(odoo_rules): | |
| result = layers.layer_knowledge(answers.LOAD_PRICES_BULLETED, odoo_rules) | |
| assert result.verdict == layers.PASS | |
| assert set(result.checked) == BULLETED_IDS | |
| # ------------------------------------------------------ context: no false pass | |
| def _ctx(**kw): | |
| base = {"source": "file", "stack": "odoo", "odoo_series": None, | |
| "manifest": None, "ts_strict": None} | |
| base.update(kw) | |
| return base | |
| def test_context_with_nothing_checkable_is_unknown(odoo_rules): | |
| """A file was supplied, and every question this layer asks came back | |
| unanswerable. It used to return PASS on zero checks -- an ok verdict for | |
| work it had not done. | |
| The rules are stripped of odoo_versions rather than taken as they ship. | |
| Declaring one is a check this layer CAN answer, so the first rule in the | |
| corpus to carry a version range would turn this test red without anything | |
| being wrong -- which is what happened when one was briefly added. | |
| """ | |
| unversioned = [dict(r, odoo_versions=None) for r in odoo_rules] | |
| result = layers.layer_context(unversioned, _ctx()) | |
| assert result.verdict == layers.UNKNOWN | |
| assert result.checked == [] | |
| assert result.not_checked | |
| def test_context_checks_a_declared_version_range(odoo_rules): | |
| """The other half of the same branch: a rule carrying odoo_versions is a | |
| question this layer can answer, and answering one is what separates PASS | |
| from UNKNOWN.""" | |
| versioned = [dict(odoo_rules[0], odoo_versions="17.0-18.0")] | |
| result = layers.layer_context(versioned, _ctx(odoo_series="16.0", | |
| manifest="__manifest__.py")) | |
| assert any("17.0-18.0" in c for c in result.checked) | |
| assert any("this module is 16.0" in f.message for f in result.findings) | |
| def test_context_with_no_file_is_unknown(odoo_rules): | |
| result = layers.layer_context(odoo_rules, {"source": "none", "stack": "odoo"}) | |
| assert result.verdict == layers.UNKNOWN | |
| def test_context_that_actually_checked_something_can_pass(odoo_rules): | |
| result = layers.layer_context(odoo_rules, _ctx(odoo_series="18.0", | |
| manifest="__manifest__.py")) | |
| assert result.checked | |
| assert result.verdict in (layers.PASS, layers.WARN) | |
| def test_context_mismatched_series_is_a_gap_and_not_a_finding(odoo_rules): | |
| """The corpus was checked against 18.0 and a 15.0 module deserves to be told | |
| -- under 'Not checked', which is where things that are true of the corpus | |
| rather than of the answer belong. | |
| It was a finding, and as a finding it was the floor: no module off the | |
| newest series could reach PASS however good the answer was, on any run. A | |
| verdict that fires every time stops being read, and none of the 15 Odoo | |
| rules carries an odoo_versions field, so the blanket warning was standing in | |
| for per-rule data that does not exist yet. | |
| """ | |
| applying = [r for r in odoo_rules | |
| if layers._version_applies(r.get("odoo_versions") or ">=16.0", "17.0")] | |
| result = layers.layer_context(applying, _ctx(odoo_series="17.0", | |
| manifest="__manifest__.py")) | |
| assert result.findings == [] | |
| assert any("17.0" in why and "18.0" in why for _, why in result.not_checked) | |
| # And it is not UNKNOWN either, because every rule now declares the series it | |
| # holds for, so there is a question here this layer can actually answer. | |
| assert result.verdict == layers.PASS | |
| def test_a_retrieved_rule_for_a_later_series_is_a_finding(odoo_rules): | |
| """The per-rule check, exercised by a real rule for the first time: the | |
| _sql_constraints rule holds from 19, and grounding a 17.0 module with it | |
| would tell the model to use a spelling 17.0 does not have.""" | |
| later = [r for r in odoo_rules if r["id"] == "odoo.sql-constraints-are-ignored-from-19"] | |
| result = layers.layer_context(later, _ctx(odoo_series="17.0", | |
| manifest="__manifest__.py")) | |
| assert [f.rule for f in result.findings] == ["odoo.sql-constraints-are-ignored-from-19"] | |
| assert layers.layer_context(later, _ctx(odoo_series="19.0", | |
| manifest="__manifest__.py")).findings == [] | |
| assert any("odoo_versions" in c for c in result.checked) | |
| def test_context_still_warns_about_a_rule_that_does_not_apply(odoo_rules): | |
| """The half that survived the demotion, and the half that earns a finding: | |
| this one names a rule that was retrieved and genuinely does not hold here. | |
| It fires on the shipped corpus now rather than on a doctored rule: every | |
| Odoo rule declares >=16.0, so a module on 15.0 gets one finding per rule | |
| instead of one blanket warning that said the same thing about all of them. | |
| """ | |
| result = layers.layer_context(odoo_rules[:3], _ctx(odoo_series="15.0", | |
| manifest="__manifest__.py")) | |
| assert result.verdict == layers.WARN | |
| assert len(result.findings) == 3 | |
| assert all(f.rule for f in result.findings), "and each one names its rule" | |
| def test_lifted_fragment_moves_imports_out_of_findings(odoo_rules): | |
| """Two medium findings for imports a method fragment could never carry were | |
| enough to turn this correct answer into WARN.""" | |
| result = layers.layer_semantic(answers.CROSS_TABLE_HONEST, "odoo", odoo_rules) | |
| assert not any("never imported" in f.message for f in result.findings) | |
| assert any("imports for curated symbols" in subject | |
| for subject, _ in result.not_checked) | |
| def test_bound_constraint_is_reported_against_its_rule(odoo_rules): | |
| result = layers.layer_semantic(answers.NEGATIVE_QTY, "odoo", odoo_rules) | |
| hits = [f for f in result.findings if f.rule == "odoo.prefer-sql-constraint"] | |
| assert len(hits) == 1 | |
| assert hits[0].severity == "high" | |
| # ----------------------------------------------------------- the whole stack | |
| def test_honest_answer_passes_every_layer(odoo_rules, bundle): | |
| results = layers.run_layers(answers.CROSS_TABLE_HONEST, "odoo", odoo_rules, | |
| bundle=bundle) | |
| decision = layers.decide(results) | |
| assert decision.verdict == "PASS" | |
| def test_false_claim_is_no_longer_a_pass(odoo_rules, bundle): | |
| """The regression this suite exists for: same code, same rules, one lie.""" | |
| results = layers.run_layers(answers.CROSS_TABLE_FALSE_CLAIM, "odoo", | |
| odoo_rules, bundle=bundle) | |
| decision = layers.decide(results) | |
| assert decision.verdict == "WARN" | |
| def test_broken_code_skips_downstream_layers_as_unknown(odoo_rules, bundle): | |
| """Findings computed over a broken parse are artefacts. They must be absent | |
| AND declared absent, not quietly counted as clean.""" | |
| results = layers.run_layers(answers.BROKEN_PYTHON, "odoo", odoo_rules, | |
| bundle=bundle) | |
| by_layer = {r.layer: r for r in results} | |
| assert by_layer["syntactic"].verdict == layers.FAIL | |
| for name in ("semantic", "domain", "empirical", "adversarial"): | |
| assert by_layer[name].verdict == layers.UNKNOWN | |
| assert by_layer[name].findings == [] | |
| def test_every_layer_reports_once(odoo_rules, bundle): | |
| results = layers.run_layers(answers.CROSS_TABLE_HONEST, "odoo", odoo_rules, | |
| bundle=bundle) | |
| names = [r.layer for r in results] | |
| assert names.count("syntactic") == 1 | |
| assert set(names) == {"syntactic", "structural", "semantic", "knowledge", | |
| "domain", "context", "empirical", "adversarial", | |
| "provenance"} | |
| def test_confidence_is_never_collapsed_to_one_number(odoo_rules, bundle): | |
| """Averaging the four readings produces a figure that is high whenever the | |
| cheap layers ran, which is the false reassurance this design refuses.""" | |
| results = layers.run_layers(answers.CROSS_TABLE_HONEST, "odoo", odoo_rules, | |
| bundle=bundle) | |
| confidence = layers.decide(results).confidence | |
| assert set(confidence) == {"structural", "semantic", "contextual", "empirical"} | |
| def test_non_python_stacks_declare_what_they_cannot_check(stack, answer, | |
| all_rules, bundle): | |
| """No TypeScript parser in the standard library, so the semantic layer must | |
| say so rather than return a clean sheet.""" | |
| rules = [r for r in all_rules if r["stack"] == stack] | |
| results = layers.run_layers(answer, stack, rules, bundle=bundle) | |
| semantic = {r.layer: r for r in results}["semantic"] | |
| assert semantic.verdict == layers.UNKNOWN | |
| assert semantic.not_checked | |
| # ------------------------------------------- coverage is evidence, or its lack | |
| def test_a_partly_reviewed_file_cannot_return_a_bare_pass(odoo_rules): | |
| """A review of 11 of a file's 20 symbols is thin evidence about the file. | |
| Measured: `check` returned PASS on a 7,929-character module having been | |
| shown 74% of it, five runs out of five, with nothing in the verdict saying | |
| so. The prose above the answer said it; the verdict is what gets read. | |
| """ | |
| clean = [layers.LayerResult("syntactic", layers.PASS, [], ["parses"], [])] | |
| ctx = _ctx(reviewed={"how": "by symbol", "shown": 11, "total": 20, | |
| "omitted": 9, "partial": None}) | |
| decision = layers.decide(clean, ctx) | |
| assert decision.verdict == "PASS", "the checks that ran still found nothing" | |
| assert decision.insufficient == ["9 of 20 symbols were not reviewed"] | |
| assert "INSUFFICIENT EVIDENCE" in layers.render(clean, decision) | |
| def test_a_whole_file_review_says_nothing_about_coverage(odoo_rules): | |
| clean = [layers.LayerResult("syntactic", layers.PASS, [], ["parses"], [])] | |
| ctx = _ctx(reviewed={"how": "whole file", "shown": 0, "total": 0, | |
| "omitted": 0, "partial": None}) | |
| assert layers.decide(clean, ctx).insufficient == [] | |
| def test_a_cut_symbol_is_reported_too(odoo_rules): | |
| clean = [layers.LayerResult("syntactic", layers.PASS, [], ["parses"], [])] | |
| ctx = _ctx(reviewed={"how": "by symbol", "shown": 2, "total": 2, | |
| "omitted": 0, "partial": "Thing"}) | |
| assert layers.decide(clean, ctx).insufficient == ["Thing was cut part-way"] | |
| def test_a_missing_layer_still_reads_as_one(odoo_rules): | |
| """The other half of the same line, and it kept its wording.""" | |
| unknown = [layers.LayerResult("domain", layers.UNKNOWN, [], [], [])] | |
| decision = layers.decide(unknown) | |
| assert decision.insufficient == ["the domain layer could not run"] | |
| def test_bandit_marker_is_read_like_the_others(): | |
| """`# nosec B608` is bandit's, and the argument for reading it is the one | |
| that put nosemgrep on the list: an independent analyser raised something | |
| and a person decided to keep the code. | |
| Measured while adding odoo.sql-needs-query-parameters: two of its six | |
| findings on OpenSPP sat on lines carrying nothing but `# nosec B608`, and | |
| were being re-raised as news on every run. | |
| """ | |
| import corpus | |
| rules = [r for r in corpus.load_rules() | |
| if r["id"] == "odoo.sql-needs-query-parameters"] | |
| body = ( | |
| "def run(self, table):\n" | |
| " self.env.cr.execute(\n" | |
| " f\"SELECT COUNT(*) FROM {table}\", # nosec B608\n" | |
| " )\n" | |
| ) | |
| hits = layers.scan_source(body, rules, "odoo", errors=[]) | |
| assert len(hits) == 1 | |
| assert hits[0].suppressed, "a bandit marker is somebody's decision too" | |
| def test_an_advisory_rule_does_not_move_the_semantic_verdict(): | |
| """An advisory rule states a preference, so its finding is reported and the | |
| layer's verdict does not follow it. Dropping the finding would be the | |
| easier change and the wrong one: the reader still wants to see it.""" | |
| import corpus | |
| rules = [r for r in corpus.load_rules() | |
| if r["id"] == "odoo.prefer-sql-constraint"] | |
| assert rules and rules[0].get("advisory") is True | |
| answer = ( | |
| "```python\n" | |
| "@api.constrains('client_id')\n" | |
| "def _check_unique(self):\n" | |
| " for rec in self:\n" | |
| " if self.search([('client_id', '=', rec.client_id)], limit=1):\n" | |
| " raise ValidationError('unique')\n" | |
| "```\n") | |
| result = layers.layer_semantic(answer, "odoo", rules) | |
| assert result.findings, "the finding is still reported" | |
| assert result.verdict == layers.PASS, "and it does not move the verdict" | |
| ADVISORY_ANSWER = ( | |
| "```python\n" | |
| "@api.constrains('client_id')\n" | |
| "def _check_unique(self):\n" | |
| " for rec in self:\n" | |
| " if self.search([('client_id', '=', rec.client_id)], limit=1):\n" | |
| " raise ValidationError('unique')\n" | |
| "```\n") | |
| def test_an_advisory_rule_does_not_move_the_domain_verdict(): | |
| """The domain layer runs the same checkers as the semantic layer and let | |
| an advisory one decide, so the semantic layer's PASS on a preference came | |
| back as the domain layer's WARN (3.77). Reported, and deciding nothing.""" | |
| import corpus | |
| rules = [r for r in corpus.load_rules() | |
| if r["id"] == "odoo.prefer-sql-constraint"] | |
| result = layers.layer_domain(ADVISORY_ANSWER, rules) | |
| assert result.findings, "the finding is still reported" | |
| assert all(f.advisory for f in result.findings) | |
| assert result.verdict == layers.PASS, "and it does not move the verdict" | |
| def test_an_advisory_finding_does_not_move_the_decision(): | |
| """decide reads the findings rather than the layer verdicts, so it has to | |
| set an advisory one aside itself. It did not: a layer that passed on a | |
| preference still made the decision WARN (3.81).""" | |
| preference = layers.Finding("high", "prefer a CHECK", | |
| rule="odoo.prefer-sql-constraint", advisory=True) | |
| defect = layers.Finding("high", "a real one", rule="odoo.ondelete-is-a-decision") | |
| alone = [layers.LayerResult("semantic", layers.PASS, [preference])] | |
| assert layers.decide(alone).verdict == "PASS" | |
| assert layers.decide(alone).reasons == [], "a preference is no reason" | |
| beside = [layers.LayerResult("semantic", layers.WARN, [preference, defect])] | |
| assert layers.decide(beside).verdict == "WARN" | |
| def test_a_finding_graded_advisory_gates_nothing_in_an_answer_either(): | |
| """Advisory follows the grade per finding as well as per rule, and one | |
| definition serves a file and an answer. legacy_sql_constraints_on_19 | |
| grades a bare snippet unproven, since nothing was walked to say which | |
| series it is for. scan_source always set that finding aside; the answer | |
| layers read the rule's flag alone and let it decide (3.81).""" | |
| import corpus | |
| rules = [r for r in corpus.load_rules() | |
| if r["id"] == "odoo.sql-constraints-are-ignored-from-19"] | |
| assert rules and not rules[0].get("advisory"), "the grade does it here" | |
| code = ("from odoo import models\n" | |
| "\n" | |
| "class Line(models.Model):\n" | |
| " _name = 'x.line'\n" | |
| " _sql_constraints = [('qty', 'CHECK(qty >= 0)', 'no')]\n") | |
| hits = layers.scan_source(code, rules, "odoo") | |
| assert hits and all(h.advisory for h in hits) | |
| answer = "```python\n" + code + "```\n" | |
| for result in (layers.layer_semantic(answer, "odoo", rules), | |
| layers.layer_domain(answer, rules)): | |
| assert result.findings, result.layer | |
| assert all(f.advisory for f in result.findings), result.layer | |
| assert result.verdict == layers.PASS, result.layer | |
| def test_one_hit_that_gates_is_enough_for_the_rule_to_gate(): | |
| """The domain layer has one finding per rule, and it is advisory only when | |
| every hit behind it is. A sudo().write() is graded unproven on its own; a | |
| sudo().search() beside it is not, and that one decides.""" | |
| import corpus | |
| rules = [r for r in corpus.load_rules() | |
| if r["id"] == "odoo.sudo-bypasses-record-rules"] | |
| touch = " self.env['res.partner'].sudo().write({'x': 1})\n" | |
| query = " return self.env['res.partner'].sudo().search([])\n" | |
| alone = "```python\ndef go(self):\n" + touch + "```\n" | |
| result = layers.layer_domain(alone, rules) | |
| assert [f.advisory for f in result.findings] == [True] | |
| assert result.verdict == layers.PASS | |
| beside = "```python\ndef go(self):\n" + touch + query + "```\n" | |
| result = layers.layer_domain(beside, rules) | |
| assert [f.advisory for f in result.findings] == [False] | |
| assert result.verdict == layers.FAIL | |
| def test_an_advisory_finding_is_labelled_where_it_is_printed(): | |
| """A PASS printed under a list of findings would read as a contradiction, | |
| so the finding says it gates nothing and the PASS line says what it | |
| passed over.""" | |
| import corpus | |
| rules = [r for r in corpus.load_rules() | |
| if r["id"] == "odoo.prefer-sql-constraint"] | |
| results = [layers.layer_semantic(ADVISORY_ANSWER, "odoo", rules), | |
| layers.layer_domain(ADVISORY_ANSWER, rules)] | |
| decision = layers.decide(results) | |
| assert decision.verdict == "PASS" | |
| out = layers.render(results, decision) | |
| assert out.count("advisory, gates nothing") == 2 | |
| assert "found nothing but advisory findings" in out | |
| # ------------------------------------------- a file the checkers cannot read | |
| PARSES = """total = fields.Float(compute='_c', store=True) | |
| @api.depends('line_ids') | |
| def _c(self): | |
| for rec in self: | |
| rec.total = sum(l.price for l in rec.line_ids) | |
| """ | |
| DOES_NOT_PARSE = PARSES + """ | |
| def oops(: | |
| pass | |
| """ | |
| def test_a_file_that_does_not_parse_is_reported_rather_than_read_as_clean(): | |
| """Every semantic checker parses for itself and returns [] when that fails, | |
| so a file with one syntax error in it produced no findings and no | |
| complaint -- which reads as a clean file, and exited 0. | |
| The findings really are unavailable. Saying so is the whole difference | |
| between a gap and a false pass, and this command exists to not have the | |
| second one. | |
| """ | |
| import corpus | |
| rules = [r for r in corpus.load_rules() if r["stack"] == "odoo"] | |
| gaps = [] | |
| found = layers.scan_source(PARSES, rules, "odoo", gaps=gaps) | |
| assert any(h.rule == "odoo.stored-compute-needs-complete-depends" | |
| for h in found), "nothing here for the broken version to lose" | |
| assert gaps == [], "a file that parses reports no gap" | |
| gaps = [] | |
| found = layers.scan_source(DOES_NOT_PARSE, rules, "odoo", gaps=gaps) | |
| assert not any(h.rule == "odoo.stored-compute-needs-complete-depends" | |
| for h in found), "the AST finding cannot survive the parse" | |
| assert gaps, "an unparseable file said nothing at all" | |
| assert "does not parse" in gaps[0] and "line 8" in gaps[0] | |
| def test_version_bounds_narrow_each_other_and_alternatives_do_not(): | |
| """A comma means two different things either side of a comparator. | |
| Read as one thing, '>=16.0,<=18.0' matched 25.0, because 25 satisfies the | |
| first half and any clause matching was enough. Nothing in the corpus writes | |
| a bounded range yet -- all 19 versioned rules declare '>=16.0' -- so this | |
| settles what the first one will mean rather than fixing a live miss. | |
| """ | |
| assert layers._version_applies(">=16.0,<=18.0", "17.0") | |
| assert not layers._version_applies(">=16.0,<=18.0", "25.0") | |
| assert not layers._version_applies(">=16.0,<=18.0", "15.0") | |
| # Bare versions cannot be an AND: nothing is both 16.0 and 17.0. | |
| assert layers._version_applies("16.0,17.0", "17.0") | |
| assert not layers._version_applies("16.0,17.0", "18.0") | |
| # And the spelling every rule in the corpus actually uses. | |
| assert layers._version_applies(">=16.0", "19.0") | |
| assert not layers._version_applies(">=16.0", "15.0") | |
| # A series nothing can parse still applies the rule, rather than dropping it. | |
| assert layers._version_applies(">=16.0", "saas~16.3") | |
| # ------------------------------------------- what a marker is about | |
| def test_a_marker_naming_another_rule_does_not_silence_this_one(): | |
| """Classifying 50 findings by hand turned up one suppression that should | |
| not have fired: a consent-receipt timestamp inside a return statement | |
| carrying `# nosemgrep: odoo-sudo-without-context` four lines below it. A | |
| marker about sudo was silencing a finding about datetimes, which is the | |
| mute button this project says it will not build, reached from the other | |
| direction.""" | |
| import corpus | |
| rules = [r for r in corpus.load_rules() | |
| if r["id"] == "python.naive-and-aware-datetimes-do-not-compare"] | |
| body = ( | |
| "from datetime import datetime\n" | |
| "from fastapi import APIRouter\n\n\n" | |
| "def receipt(self):\n" | |
| " return ConsentReceipt(\n" | |
| " issued=datetime.utcnow(),\n" | |
| " who=self.env.user.id,\n" | |
| " ) # nosemgrep: odoo-sudo-without-context\n" | |
| ) | |
| hits = layers.scan_source(body, rules, "odoo", errors=[]) | |
| assert len(hits) == 1 | |
| assert not hits[0].suppressed, "a marker about sudo does not speak for datetimes" | |
| def test_a_marker_naming_this_rule_still_silences_it(): | |
| """The other side of the same line, and the measured one: 105 of OpenSPP's | |
| 106 `.sudo().search(` findings carry a nosemgrep marker about that exact | |
| construct. `odoo-sudo-without-context` and `odoo.sudo-bypasses-record-rules` | |
| meet on `sudo` -- and not on `odoo`, which names a vendor rather than a | |
| defect.""" | |
| import corpus | |
| rules = [r for r in corpus.load_rules() | |
| if r["id"] == "odoo.sudo-bypasses-record-rules"] | |
| body = ( | |
| "def f(self):\n" | |
| " return self.env['res.partner'].sudo().search(\n" | |
| " [('active', '=', True)],\n" | |
| " ) # nosemgrep: odoo-sudo-without-context\n" | |
| ) | |
| hits = layers.scan_source(body, rules, "odoo", errors=[]) | |
| assert len(hits) == 1 | |
| assert hits[0].suppressed | |
| def test_an_opaque_code_covers_the_line_as_it_always_did(): | |
| """`B608` carries no words, so it cannot be compared with a rule id at | |
| all. Narrowing on a comparison that cannot be made would drop a decision | |
| somebody really did record -- two of the six sql-needs-query-parameters | |
| findings on OpenSPP sit on lines carrying nothing but `# nosec B608`. The | |
| gap this leaves is named in _marker_is_about.""" | |
| carries, codes = layers._marker_scope(" x = 1 # nosec B608") | |
| assert carries and codes == set() | |
| def test_prose_after_a_marker_is_not_read_as_a_rule_name(): | |
| """People write sentences there. `# noqa: decided, TICKET-9` is a person | |
| talking, not an analyser code, and reading `decided` as a rule id would | |
| silently unsuppress a line somebody had settled.""" | |
| for text in (" x = 1 # noqa: decided, TICKET-9", | |
| " x = 1 # noqa: this guard is advisory, see TICKET-41"): | |
| carries, codes = layers._marker_scope(text) | |
| assert carries and codes == set(), text | |
| def test_a_statement_that_covers_the_line_beats_one_that_borrows_it(): | |
| """Every span reaches one line above its statement to take in a marker | |
| written there, so two statements on consecutive lines overlap. A finding | |
| on the first line of one is also inside the borrowed line of the one | |
| below, both spans are one line wide, and the tie used to go to whichever | |
| ast.walk reached first. | |
| Found on OpenSPP the first time the write clause of the sudo rule fired: | |
| spp_api_v2/wizards/show_secret_wizard.py carries its nosemgrep on line 36 | |
| about the write on 37, and the `return` on 38 borrows 37 for itself. The | |
| marker read as absent and a decision somebody had recorded was re-raised. | |
| """ | |
| import corpus | |
| rule = [r for r in corpus.load_rules() | |
| if r["id"] == "odoo.sudo-bypasses-record-rules"][0] | |
| body = ( | |
| "def action_close(self):\n" | |
| " if self.client_id:\n" | |
| " # nosemgrep: odoo-sudo-without-context -- wizard ACL controls access\n" | |
| " self.client_id.sudo().write({'client_secret': False})\n" | |
| " return {'type': 'ir.actions.act_window_close'}\n" | |
| ) | |
| hits = layers.scan_source(body, [rule], "odoo", errors=[]) | |
| assert len(hits) == 1 and hits[0].line == 4 | |
| assert hits[0].suppressed, "the marker one line above its own statement was lost" | |
| def test_an_xml_file_is_read_as_xml_not_reported_unparseable_python(): | |
| """Every XML file used to fail to parse AS PYTHON, and was reported "not | |
| fully checked" with 15 semantic checkers never run -- on a valid file.""" | |
| import corpus | |
| body = ('<?xml version="1.0"?>\n<odoo>\n' | |
| ' <record id="p" model="ir.config_parameter">\n' | |
| ' <field name="key">k</field>\n </record>\n</odoo>\n') | |
| rules = [r for r in corpus.load_rules() if r["stack"] == "odoo"] | |
| gaps = [] | |
| hits = layers.scan_source(body, rules, "odoo", gaps=gaps) | |
| assert gaps == [] | |
| assert [(h.rule, h.line) for h in hits] == [("odoo.noupdate-on-user-editable-data", 3)] | |
| def test_python_rules_do_not_run_over_xml_and_the_xml_rule_not_over_python(): | |
| assert layers.source_kind("<!-- licence -->\n<odoo>\n</odoo>\n") == "xml" | |
| assert layers.source_kind("from odoo import models\n") == "python" | |
| assert layers.source_kind("# <odoo> in a comment\nx = 1\n") == "python" | |
| def test_malformed_xml_is_a_gap_not_a_clean_file(): | |
| import corpus | |
| rules = [r for r in corpus.load_rules() if r["stack"] == "odoo"] | |
| gaps = [] | |
| assert layers.scan_source("<odoo>\n<record>\n</odoo>\n", rules, "odoo", gaps=gaps) == [] | |
| assert gaps and "does not parse as XML at line 3" in gaps[0] | |