Protected in Name Only

The settings page said the branch was protected. It listed the checks that had to pass before a change could be accepted, and every one of them was green. Then I pushed a change straight onto the main branch, and it landed without a single one of those checks being consulted. Not failed. Not skipped with a warning. Never evaluated at all.

TL;DR: Requiring automated checks before a change is accepted only applies to changes submitted for review. A change pushed directly onto the main branch bypasses them entirely, because there is no review for the requirement to attach to. A separate setting is what actually blocks direct pushes. A repository with required checks configured and direct pushes still allowed looks protected and is not, and the only way to know which one you have is to push something and watch it get stopped.

What Is Branch Protection?

Most software teams keep their code in a shared repository, with one main copy that represents what is live. Branch protection is the set of rules that decide who can change that main copy and under what conditions. In a healthy setup, nobody edits it directly. You propose a change, automated checks run against the proposal, someone or something confirms it is safe, and only then does it get merged in.

That is the theory. In practice the settings are several independent switches that sound like they do the same job, and they do not. One says which automated checks are required. A different one says whether a person can skip the whole process and write to the main copy directly. Turning on the first without turning off the second gives you a page full of reassuring green labels protecting nothing.

I Did Not Take the Documentation's Word for It

The behavior I described is documented. It is also easy to misread, and I have been wrong about settings pages before, so I built a throwaway repository to check.

I configured it exactly like the real ones: required checks listed, direct pushes still permitted. Then I pushed a commit straight to the main branch. It went through, and the required check sat there configured and unrun. Then I flipped the one setting that actually governs direct writes, and pushed the identical commit again. Rejected, before it ever reached the repository.

That is the standard I try to hold every safety control to, and it is the reason I bothered with a throwaway repository at all. A control is not verified because the documentation describes it, or because the interface displays it, or because nothing bad has happened yet. It is verified when you have watched it stop something you deliberately aimed at it. Everything short of that is a belief.

Three Live Sites Were in That Exact State

Once I knew what to look for, I read the configuration of every repository behind a live site instead of clicking through the pages one at a time.

Three had required checks configured and direct pushes still allowed. All three looked protected to anyone glancing at the settings, and none of them were. Two more had no protection at all, not even the decorative kind.

None of this was a deliberate decision. It was drift. Repositories get created at different times, by copying whatever the previous one looked like, and the setting that matters is the one you cannot see from the outside. Two of my projects were set up correctly, and the difference was not care or attention. It was which template happened to be in front of me that day.

The Bigger Hole Nobody Was Looking At

All of that attention was aimed at the repositories holding the application code, because that is the obvious place to look. The code is what you think of as the product.

The repositories holding the deployment configuration had no protection whatsoever. Eight of them. Those are the files that define what actually runs in production: which version of the software gets started, which settings it receives, what it is allowed to reach. A change to one of those is a production change with nothing standing in front of it. No review, no checks, no second look.

The application repositories at least had something, however hollow. The deployment configuration had nothing, and it stayed that way precisely because everyone, including me, was watching the more visible thing. The most exposed part of my setup was the part nobody had thought to audit, because it did not look like software. It looked like configuration.

That pattern is not specific to me. If you want to find the weak point in an organization's controls, look at the systems that got treated as plumbing.

Written Rules Do Not Enforce Anything

There is a second failure here, and it is the one I find most uncomfortable, because I had put real effort into the thing that failed.

I maintain a playbook of standards for how my projects should be built and operated. Roughly 5,800 lines of it, written carefully, phrased firmly, full of sentences with the word "must" in them. There was even an automated gate attached, which is the part that made it feel finished. The gate executed nothing. It ran, reported success, and checked no rule at all.

The standards were real and the enforcement was theatre. Every one of the misconfigurations above was already forbidden in writing. Writing did not stop any of them.

The distinction worth taking away is simple. A documented standard is opt-in. It depends on somebody remembering it, at the right moment, under time pressure, on a day when nothing is on fire. A guardrail is mandatory and checked by a machine, and it does not care whether anybody remembered. Rules in a document are the first kind no matter how firmly they are worded, and the length of the document does not move them toward the second.

The fix was to stop writing and start converting. About twenty-five of those rules became checks that actually run and actually fail. That single pass took the number of failing conditions across my projects from twenty-two down to five, and the five that remain are things I have deliberately deferred rather than things I do not know about. That is the real change: the unknown became a list.

Two Details Worth Keeping

Two design decisions came out of that work that I would carry into any similar system.

A checker must print what it measured. A count of problems is worthless without the identity of the thing it examined, because the same command run against a slightly different version of the same project produces a different number. A finding count with no statement of what was inspected is a rumor with a number attached.

A checker that cannot measure something must say so out loud. If the credentials are missing, or the data it needs is not present, it has to report "not run" rather than finishing quietly. The temptation is to let it pass, because nothing went wrong. But "I looked and found no problems" and "I never looked" have to produce visibly different output, or you will mistake one for the other at the worst possible moment. Cannot measure must never render as clean.

The Uncomfortable Part

Here is the line I keep coming back to. This was the third time in five days that an audit found a protection setting configured wrong.

Not the third ever. The third in five days, across systems I built, using standards I wrote. That is the actual lesson, and it is not about repositories at all.

Setting a control up correctly once is not the same as having it. Settings drift. Someone changes a related option and a dependency quietly resets. A new project is created from an older template. A platform update introduces a switch that defaults to permissive. None of that announces itself, and none of it shows up as a failure, because a control that has stopped working looks exactly like a control that has nothing to stop.

Something has to re-check the controls on a schedule, and that something has to be a program rather than an intention. This is the same argument I make about automating the boring parts of infrastructure work and about running your own systems deliberately rather than by accident. The value is never in the initial setup. It is in whether anything notices when the setup quietly stops being true.

Three Questions for Your Own Business

You do not need to be technical to ask these, and the answers are revealing.

Which of our safety controls has anyone actually watched block something? Not configured, not documented, not displayed on a settings page. Watched. If nobody can describe the time it stopped a change, that control's honest status is unknown rather than working.

What re-checks those controls, and when did it last run? If the answer is that someone set them up carefully at the start, you have a snapshot of a moment that has since passed.

Are the systems holding our deployment configuration protected as carefully as the ones holding our code? In my experience, this is where the gap lives, because that layer rarely gets treated as software even though it decides what production actually runs.

If you are not sure what your own controls actually enforce, that is a much better thing to discover on a quiet afternoon than during an incident. Get in touch and we can go through it together.

Share on LinkedIn

About Etherion Tech

Etherion Tech is an independent IT infrastructure and automation consultancy based in Tulsa, Oklahoma, with over 10 years of experience in systems administration, identity and access management, cloud migration, and process automation. Certifications include CompTIA Security+, Network+, A+, ITIL v4, Azure Fundamentals, and Linux Essentials.

More about the practice · Automation work · Get in touch