Skip to content

A format rule with an invalid regex, and a json_schema rule ajv cannot compile, still fail OPEN — the same trap #4649 closed, one rule type over #4762

Description

@os-zhuang

Found while implementing #4649 (PR #4761). Filed unassigned, not fixed there — #4649's ruling scoped itself to the object-level predicates, and widening it in the same PR would have mixed two blast radii.

What is still fail-open

packages/objectql/src/validation/rule-validator.ts:

  • checkFormat — a regex that new RegExp(...) throws on is logged and skipped:
    Validation rule '<name>' has an invalid regex — skipped
    
  • checkJsonSchema — a schema ajv.compile(...) throws on is logged and skipped:
    Validation rule '<name>' has an uncompilable JSON Schema — skipped
    

Both are the exact shape #4649 was filed about: the rule is declared, appears in the metadata, appears in any "what protects this object" listing, and enforces nothing — the only signal being a WARN in a log nobody reads. #4761 flipped the CEL predicates (script / cross_field / conditional.when) to fail closed and left these two deliberately unchanged, with a test pinning that they still fail open so the gap is visible rather than assumed closed:

packages/objectql/src/validation/rule-fail-closed.test.ts#4649 — unchanged neighbours.

Why it is a smaller problem than #4649 was, but still a problem

A broken regex / schema is static — it is broken for every record, from the moment it is authored, not only for records of a particular shape. So it is far more likely to be noticed in dev than #4649's data-dependent fault was. That is an argument about when it is noticed, not about whether the rule enforces anything: on a deployed system it enforces nothing, silently, exactly as before.

Two ways to close it, and they are not exclusive

  1. Authoring/publish time (preferred, contract-first). A regex that does not compile and a JSON Schema ajv rejects are both decidable without a record. Rejecting them at publish — packages/lint — kills the rule class outright and never risks bricking a running deployment. This is the "declared = enforced" fix rather than a runtime policy fix.
  2. Write time, to match Script validation rules are silently skipped when their predicate fails to evaluate — fail-open is the wrong direction for a validation #4649. Reuse unevaluableRuleError from rule-validator.ts: reject with reason: 'unevaluable', naming the rule. Note the blast radius differs from Script validation rules are silently skipped when their predicate fails to evaluate — fail-open is the wrong direction for a validation #4649's in an important way — an unevaluable predicate rejects only writes whose data triggers the fault, whereas a broken regex would reject every write touching that field, for as long as the bad metadata is deployed. That is a strong argument for doing (1) first, and only then deciding whether (2) is still worth having as a backstop.

Acceptance sketch

  • A format rule with regex: '([' is rejected at publish/lint with the rule name and the regex error.
  • Same for a json_schema rule whose schema ajv cannot compile.
  • If (2) is also taken: the #4649 — unchanged neighbours tests in rule-fail-closed.test.ts flip, and the module header's "Deliberately NOT changed here" paragraph is updated in the same PR — it is the record of this decision.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions