Skip to content

Say so at startup when anyone can stop a checker without logging in - #42

Merged
ivanvyd merged 1 commit into
mainfrom
feature/unauthenticated-surface-warning
Jul 30, 2026
Merged

Say so at startup when anyone can stop a checker without logging in#42
ivanvyd merged 1 commit into
mainfrom
feature/unauthenticated-surface-warning

Conversation

@ivanvyd

@ivanvyd ivanvyd commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Closes the authorization-defaults question by keeping both defaults and making them loud.

AddHealthieController doesn't require authorization unless asked, and the dashboard's AllowMutations defaults to true. Both are deliberate — the controller ships into someone else's MVC pipeline and can't demand a policy it knows nothing about, and a read-only dashboard would make every first run look broken. Changing either breaks every application that maps them.

What was missing was anyone being told. An application that maps one of these and stops there lets whoever can reach it stop a checker, slow its interval, or clear a failing streak — hiding an incident rather than reporting one, from a tool whose whole job is reporting them.

warn: Healthie: 6 endpoint(s) that can change a pulse checker are reachable without
      authenticating -- healthie/{checkerName}/reset, healthie/{checkerName}/start, ...
      Anyone who can reach this application can stop a checker or clear a failing streak,
      which hides an incident rather than reporting one. Pass requireAuthorization: true to
      AddHealthieController, or apply your own authorization to these endpoints.

It reads the endpoints, not the flags

A host may have secured things some other way — RequireAuthorization(), an endpoint group, its own attribute. Warning at an application that is already secured is how a warning gets filtered out, and then it isn't there for the one that needs it. So the check inspects what was actually applied.

Only mutating methods are named. Reading health without authenticating is a choice an operator can reasonably make; listing those routes would drown the part that matters.

The tests earned their place

Looking only for IAuthorizeData warned at AddHealthieController(requireAuthorization: true) — because the convention adds an AuthorizeFilter, which is IFilterMetadata, not IAuthorizeData. The one configuration that had explicitly asked for authorization was the one being warned about. AnApiThatRequiresAuthorization_IsNotWarnedAt caught it.

Seven tests, driven through a real host on an OS-assigned port so the endpoints genuinely exist and carry their metadata:

mutation tests failed
api warns even when authorization is applied 2
api names read-only routes too 1
api misses the convention's AuthorizeFilter 1
dashboard warns even when read-only 1
dashboard ignores RequireAuthorization 1

473 tests green on net8.0 and net10.0, 0 errors / 0 warnings.

Scope

Healthie.Mcp is left alone — its AllowMutations defaults to false, and when off the mutating tools aren't registered at all, so there's nothing to warn about.

Logged once at startup, at Warning. It describes a configuration an operator chose and can change, not a runtime fault, so repeating it per request would bury the logs it's trying to be noticed in.

AddHealthieController does not require authorization unless it is asked to,
and the dashboard's AllowMutations defaults to true. Both are deliberate. The
controller ships into someone else's MVC pipeline and cannot demand a policy
it knows nothing about; a read-only dashboard would make every first run look
broken. Neither default is wrong, and changing either breaks every application
that maps them.

What was missing was anyone being told. An application that maps one of these
and stops there lets whoever can reach it stop a checker, slow its interval,
or clear a failing streak -- which hides an incident rather than reporting
one, from a tool whose whole job is to report them. That is worth a line in
the log at startup.

The check reads the endpoints, not the flags that built them. A host may have
applied authorization some other way -- RequireAuthorization, an endpoint
group, an attribute of its own -- and warning at an application that is
already secured is how a warning gets filtered out, after which it is not
there for the one that needs it. Only mutating methods are named, because
reading health without authenticating is a choice an operator can reasonably
make and listing it would drown the part that matters.

Both shapes had to be handled, and the tests are why that is known: looking
only for IAuthorizeData warned at AddHealthieController(requireAuthorization:
true), because the convention adds an AuthorizeFilter, which is an
IFilterMetadata and not an IAuthorizeData. The one configuration that had
asked for authorization by name was the one being warned about.

Healthie.Mcp is left alone: its AllowMutations defaults to false, and when it
is off the tools that change anything are not registered at all.
@ivanvyd
ivanvyd merged commit 00aeb7f into main Jul 30, 2026
7 of 8 checks passed
@ivanvyd
ivanvyd deleted the feature/unauthenticated-surface-warning branch July 30, 2026 02:15
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