Skip to content

fix: tighten the debug logging, rule-pattern and ingress guards - #94

Merged
rmyndharis merged 3 commits into
mainfrom
fix/logging-regex-and-catalog-gates
Aug 12, 2026
Merged

fix: tighten the debug logging, rule-pattern and ingress guards#94
rmyndharis merged 3 commits into
mainfrom
fix/logging-regex-and-catalog-gates

Conversation

@rmyndharis

Copy link
Copy Markdown
Owner

Three defects, each in a guard that was meant to prevent exactly the thing it let through.

supabase-otp-hook — debug logs carried the code and the number

Debug mode logged the composed message, which contains the verification code, and the destination chat id, which is a phone number, across three separate lines. Debug is switched on precisely when a delivery is misbehaving, so that output is what gets pasted into a support thread or shipped to a log collector — an OTP is a live credential, and the id beside it names its owner.

The send line now records the message length rather than the message, and chat ids are reduced to their last four digits: enough to correlate two lines about the same chat, not enough to identify anyone. The diagnostic still shows that a send was attempted, which is the point of debug mode.

faq-bot — two ways past the rule-pattern safety analyser

The analyser rejects patterns prone to catastrophic backtracking before they reach new RegExp. Two of its assumptions were wrong, and both were reproduced:

  • It reasoned that a small bounded repeat is bounded by its constant. True for a variable-width body, false for an unbounded one — (a+){3} expands to a+a+a+. Measured: 200 characters took 1.4 s, 1000 took over 6 s. Any repeat of an unbounded body is refused now, while (ab?){2} stays allowed, since that is the case the constant really does bound.
  • It treated every group as breaking a run of adjacent unbounded quantifiers. A group that can match empty does not, so .*(x?).*(x?).* was .*.*.* in disguise; \w*(a?)\w*(a?)\w*! took 10.6 s on 300 characters. The run is now parked at the opening paren and resumed if the group turns out nullable. A group that must consume something still breaks it, so .*(x).*(y).* remains accepted.

Overlapping alternation is still not modelled, and the doc comment still says so.

catalog — ingress declared without the permission

An ingress route becomes a public endpoint once the host provisions it, and the host refuses to load a plugin declaring one without webhook:ingress. That refusal lands at install, on the operator's gateway, after the release is tagged and published. The catalogue gate now checks it where the package is built.

Verification

  • 580 tests pass, typecheck clean, catalog up to date, all 10 plugins build and load.
  • All three are test-first. The logging test failed showing the OTP and the full number in the log dump; the analyser tests failed on the patterns above; the catalogue gate was probed with a fixture manifest that declares a route and no permission, and refused it by name.

Debug mode logged the composed message, which contains the verification code, and the destination chat
id, which is a phone number, on three separate lines. Debug is switched on precisely when a delivery is
misbehaving, so that output is what gets pasted into a support thread or shipped to a log collector — an
OTP is a live credential and the id beside it names its owner.

The send line now records the message length rather than the message, and chat ids in log lines are
reduced to their last four digits: enough to correlate two lines about the same chat, not enough to
identify anyone. The diagnostic still shows that a send was attempted, which is what debug is for.
The analyser rejects patterns prone to catastrophic backtracking before they are compiled. Two of its
assumptions were wrong.

It reasoned that a small bounded repeat is bounded by its constant. That holds for a variable-width body
but not for an unbounded one: `(a+){3}` expands to `a+a+a+`, which backtracks exponentially — 200
characters took 1.4 s and 1000 took over six. Any repeat of an unbounded body is refused now, while a
bounded repeat of a variable body like `(ab?){2}` stays allowed, since that is the case the constant
really does bound.

It also treated every group as breaking a run of adjacent unbounded quantifiers. A group that can match
empty does not, so `.*(x?).*(x?).*` was `.*.*.*` wearing a disguise. The run is now parked at the
opening paren and resumed if the group turns out to be nullable; a group that must consume something
still breaks it.

Overlapping alternation remains unmodelled and is still documented as such.
…mission

An ingress route becomes a public endpoint once the host provisions it, and the host refuses to load a
plugin that declares one without `webhook:ingress`. That refusal happens at install, on the operator's
gateway, after the release has been tagged and published. The catalogue gate now checks it where the
package is built instead.
@rmyndharis
rmyndharis merged commit 654059a into main Aug 12, 2026
1 check passed
@rmyndharis
rmyndharis deleted the fix/logging-regex-and-catalog-gates branch August 12, 2026 10:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant