nexa / tests /test_layers.py
DBax127's picture
layers: an advisory finding gates nothing in the answer layers either
5be3d19
Raw History Blame Contribute Delete
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"}
@pytest.mark.parametrize("stack,answer", [
("typescript", answers.TS_SEARCH_READ),
("javascript", answers.JS_CONFIG_LOOKUP),
])
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]