Table of Contents

Security policy

RuleWright treats a rule document as code-adjacent input. Rules decide business outcomes, so writing them is a privileged operation: review rule changes the way you review code, and don't load documents from people you haven't decided to trust. Within that assumption, the properties below are deliberate, and a break in any of them is a vulnerability.

Reporting a vulnerability

Report privately, not in a public issue. Open a security advisory on the repository, or email salwan@albahadly.com with "Security" in the subject.

Include the affected version and target framework, a rule document and a fact that reproduce the problem, and what you saw compared with what you expected. You can expect an acknowledgement within a week, then an assessment with a fix, or a rejection with reasons.

Supported versions

RuleWright is before 1.0. Fixes land in the latest release; there are no long-term support branches yet. Upgrade to get a fix.

What counts

A vulnerability is a break in any of these:

  • Rules are pure data. The vocabulary is closed: operators, field paths and literals. RuleWright never compiles, evaluates or executes a string from a document. The only extension point is a custom function that the host application registered.
  • Evaluation is total. It doesn't throw on data. A null field, a missing key, a non-numeric operand and a division by zero each have defined results, so a hostile fact can't turn an evaluation into an exception path.
  • Regular expressions are time-limited. MatchesRegex runs against consumer data with a bounded timeout (one second by default, set with UseRegexTimeout) and raises RegexMatchTimeoutException rather than pinning a thread.
  • Nesting is bounded. The JSON readers cap depth at 64 by default, so a deeply nested document can't exhaust the stack while parsing.
  • Numbers are finite. An out-of-range literal is rejected when the document is parsed, the same way on every target framework.
  • Evaluation is stateless and thread-safe. One engine serves concurrent evaluations, and a fact never leaks into another evaluation.

Known limits, by design

These are documented behaviour, not defects. Know them before you widen who may write rules:

  • The compiled-delegate cache is unbounded. It lives as long as the engine. A process that loads an unbounded number of distinct rules grows with them. Rebuild the engine periodically if rules are hot-reloaded at scale.
  • Compilation cost scales with document size. Bound the size of the documents you accept.
  • A custom function is host code. Its safety, thread safety and running time are yours.
  • Field paths read your fact. A rule can read any public property or field reachable from the fact you pass. Pass a projection built for the purpose, not a domain object with sensitive members hanging off it.

Accept rules you didn't write turns these into practice.