The View Counter That Let Anyone Write Anything
A page view counter needs writes from readers who are not signed in. That requirement is reasonable. The first implementation was not.
The rule that looked fine
allow update: if request.auth == null;
The intent was "anonymous readers may bump the counter." What it says is "anonymous readers may replace the entire document." Title, body, published flag — all writable by anyone with the project id, which ships in your client bundle.
Pinning the write
Three conditions, all necessary:
allow update: if resource != null
&& resource.data.get('published', false) == true
&& request.resource.data.diff(resource.data).affectedKeys().hasOnly(['views'])
&& request.resource.data.views is int
&& request.resource.data.views == resource.data.get('views', 0) + 1;
hasOnly blocks extra fields riding along. The equality check blocks setting the count to an arbitrary number. The published check stops drafts being probed for existence.
The lesson
An allow rule grants everything it does not forbid. Write down what the operation is permitted to change, then encode exactly that — "who" is only half a rule, "what" is the other half.