DEV Community

FlareCanary
FlareCanary

Posted on

GitHub Just Retired Seven Org Security Fields — Your 'New Repo Hardening' Script Is Now A No-Op

GitHub announced on April 21 that seven security-defaults fields on the org REST endpoint are now retired:

  • advanced_security_enabled_for_new_repositories
  • dependabot_alerts_enabled_for_new_repositories
  • dependabot_security_updates_enabled_for_new_repositories
  • dependency_graph_enabled_for_new_repositories
  • secret_scanning_enabled_for_new_repositories
  • secret_scanning_push_protection_enabled_for_new_repositories
  • secret_scanning_push_protection_custom_link_enabled

These appeared on GET /orgs/{org} and were writable on PATCH /orgs/{org}. The retirement isn't a 410 or a schema-validation error. The fields just stop appearing on reads. The PATCH still returns 200 OK whether you send them or not — it just no longer applies them.

Replacement: Code Security Configurations API (/orgs/{org}/code-security/configurations). Different endpoint, different shape, different concept — configurations are objects you attach to repositories, not booleans on the org.

This is the deepest silent-fail vector we've seen all month. It's not a billing column or a string field. It's the one-line setup script every platform-engineering team wrote to harden new repos by default.

The script that quietly stopped working

The classic version:

await octokit.orgs.update({
  org: 'acme-platform',
  advanced_security_enabled_for_new_repositories: true,
  dependabot_alerts_enabled_for_new_repositories: true,
  secret_scanning_enabled_for_new_repositories: true,
  secret_scanning_push_protection_enabled_for_new_repositories: true,
});
Enter fullscreen mode Exit fullscreen mode

Pre-2026-04-21: that PATCH applied org-wide defaults. Every new repo created in acme-platform after this call had Dependabot, advanced security, and secret scanning on at creation.

Post-retirement: that PATCH returns 200. Octokit doesn't throw. The audit log records the call. The fields are simply ignored.

A new repo created tomorrow has none of those defaults applied. The compliance script ran. The dashboard says "secret scanning enabled org-wide." A git push with an AWS access key in it goes through unchallenged.

The terraform-github-provider equivalent (github_organization_settings) wraps the same fields and has the same behavior. terraform apply says no changes. The state file thinks it's converged. The org isn't enforcing what the state file claims.

Why reads break differently from writes

GitHub's REST API doesn't versioned-deprecate fields the way it does for merge_commit_sha. There's no X-GitHub-Api-Version: 2022-11-28 you can set to keep the old field alive. The fields are just gone from GET /orgs/{org} everywhere now.

Reading code that does this:

const { data: org } = await octokit.orgs.get({ org });
if (org.secret_scanning_enabled_for_new_repositories) { /* ... */ }
Enter fullscreen mode Exit fullscreen mode

…now hits undefined. JavaScript's truthiness rules turn that into the false branch. Compliance dashboards that read these fields to display "✓ Secret scanning on" suddenly read "✗" and a security engineer pages someone, OR — worse — the dashboard caches the last known value from a week ago and keeps showing green for months.

Writing code is the more dangerous shape. The PATCH endpoint accepts arbitrary unknown keys without erroring. There's no "did this field actually apply" affordance in the response. Your script can include 47 retired-or-renamed properties and the API still returns 200 with the org object that has none of them on it. Comparing the response back is the only check, and almost nobody writes that compare.

The replacement is not a one-line swap

The Code Security Configurations API is conceptually different. Instead of:

PATCH /orgs/{org}  { secret_scanning_enabled_for_new_repositories: true }
Enter fullscreen mode Exit fullscreen mode

…you now:

  1. POST /orgs/{org}/code-security/configurations to create a configuration object (with secret scanning, Dependabot, advanced security, push protection settings as nested objects).
  2. POST /orgs/{org}/code-security/configurations/{config_id}/defaults to attach it as the default for new repos. The body specifies which repo types it applies to (new_repos, private_repos, public_repos, all).
  3. Optionally, POST /orgs/{org}/code-security/configurations/{config_id}/attach to retroactively apply it to existing repos.

The shape of the configuration object isn't a 1:1 mapping. advanced_security becomes part of a nested struct. secret_scanning_push_protection_custom_link becomes a sub-field of secret scanning. Some legacy combinations aren't expressible. Some new combinations are.

Migration is a real engineering task, not a search-and-replace. And the deprecation notice doesn't have a hard cutoff date — the fields are listed as "Retired" but reads have already broken, so there's no grace window left.

Why your tests didn't catch it

The pattern by now is familiar. Three things have to be true to catch this in CI:

  1. Your fixture for GET /orgs/{org} was regenerated against the live API after April 21 (it wasn't).
  2. Your test asserts presence (expect(org).toHaveProperty('secret_scanning_enabled_for_new_repositories')) rather than truthiness on a possibly-undefined field (most don't).
  3. Your terraform plan/apply tests run against a live GitHub org and diff the result, not against a mocked provider (almost nobody does this).

Most platform teams test their org-hardening script by spinning up a sandbox org once, validating the fields land, and never re-validating. The sandbox org's settings still look right because they were set before the retirement. The script that "still works" hasn't actually applied anything for any new repo created after April 21.

# API What changed Where tests missed it
1 GitHub PushEvent commits field silently dropped Tests didn't assert field presence
2 Stripe Basil current_period_end moved to items Tests used Checkout fixtures
3 Shopify 2025-01 fulfillmentHold type change Tests mocked the response
4 OpenAI Responses input_text removed for assistants Tests covered request role=user
5 Twilio regional Regional domains stop resolving Tests don't hit prod DNS paths
6 HubSpot Contacts v1 list-memberships returns empty Tests asserted against sandbox fixtures
7 GitHub merge_commit_sha Field removed from PR responses Tests used pre-rollout fixtures
8 GitHub org security fields 7 fields retired from /orgs/{org} Org-hardening script tested once, never re-run

Eight in a row. The breaking change is always in a field a test isn't asserting against, in a layer (transitive SDK upgrade, version-pinned header, configuration drift) the test isn't exercising, or in a script that runs once during onboarding and is never re-validated.

What to do this week

Three actions, in priority order:

  1. Grep your platform-eng repos for the seven field names. Anywhere they appear in a PATCH body or a read assertion is a candidate failure. Org-hardening Octokit calls, Terraform configs, compliance dashboards, audit-log queries — all suspect.

  2. Audit any new repository created in your org after April 21, 2026. Check whether secret scanning, Dependabot alerts, and advanced security are actually enabled on each one. If your org-default script broke silently, every new repo since then has weaker security than the dashboard claims.

  3. Migrate to Code Security Configurations. Create the configuration, attach it as a default for new repos, and — separately — attach it retroactively to repos created during the silent-fail window. The retroactive attach is the cleanup step most migrations forget.

The PATCH that returns 200 OK and does nothing is the worst kind of API regression. There's no error to alert on. The audit log shows you ran the script. Compliance reports green. And every new repo in your org for the past week has its security defaults in whatever state GitHub decided to leave them in — which, based on testing, is "not what your script set."

We built FlareCanary for exactly this layer. Snapshot the response shape on a schedule, diff it, alert when fields disappear or accept-but-ignore semantics shift. The GitHub org security retirement is the textbook case: no error, no version header, no migration warning, just seven fields that used to enforce things and now silently don't.


If your platform team's onboarding script touched any of these seven org-level fields and hasn't been audited since April 21, your org is shipping new repos with whatever default security GitHub decided on — not what your runbook says. Worth a Tuesday afternoon.

Top comments (0)