Skip to content

[workers-utils] Stop container key validation from crashing on malformed input - #15088

Open
Neal006 wants to merge 3 commits into
cloudflare:mainfrom
Neal006:fix/container-key-validation-crash
Open

[workers-utils] Stop container key validation from crashing on malformed input#15088
Neal006 wants to merge 3 commits into
cloudflare:mainfrom
Neal006:fix/container-key-validation-crash

Conversation

@Neal006

@Neal006 Neal006 commented Aug 8, 2026

Copy link
Copy Markdown

Fixes #15087.

Validating a containers entry pushed a type error into diagnostics and then dereferenced the value it had just rejected. Five malformed configs therefore threw a raw TypeError out of normalizeAndValidateConfig. None of them is a UserError, so handle-errors.ts printed a stack trace and "If you think this is a bug, please open an issue" instead of naming the offending key.

Three root causes, all in validateContainerApp:

  • public_key dereferenced after being rejected. if (!isRequiredProperty(key, "public_key", "string")) recorded the error, then the next statement called key.public_key.toLowerCase() unconditionally. Missing public_key threw Cannot read properties of undefined (reading 'toLowerCase'); a non-string one threw key.public_key.toLowerCase is not a function.
  • Non-object entries. isRequiredProperty bottoms out in hasProperty, which is property in target, and in throws on a primitive. authorized_keys: ["ssh-ed25519 AAAA..."] threw before any check could report anything.
  • configuration: null. The guard was typeof configuration !== "object" || Array.isArray(configuration), and typeof null === "object", so null passed as a valid object and Object.keys(null) threw about 240 lines later.

The authorized_keys and trusted_user_ca_keys blocks were copies of each other differing only in whether name is required, so both carried both key faults. They are now one validateSshPublicKeys helper: it reports a non-array up front, skips entries that are not objects, and makes the ED25519 prefix check an else if so it only runs on a public_key that already passed the string check. The configuration type check gains the missing null case, and the later Object.keys call is guarded so it only runs for a real object, which also removes the bogus Unexpected fields found in containers.configuration field: "0","1","2" warning a string value produced from its character offsets.

All of these now produce ordinary diagnostics such as containers.authorized_keys[0].public_key must be a string. Net effect on the source is 28 fewer lines.

Addressed from review

  • configuration: null combined with instance_type still crashed. The type check reported the problem and then fell through to the limits-versus-instance_type check, which dereferences configuration.disk, .vcpu and .memory_mib, throwing TypeError: Cannot read properties of null (reading 'disk'). That is the same class of crash this PR exists to remove, and my first pass missed it. The limits check is now an else branch of the type check, so it only runs once configuration is known to be a real object.
  • Article agreement in the unsupported-key-type message. It read "is a unsupported key type. Please provide a ED25519 public key" and now reads "an" in both places. This is the one existing message the PR changes; nothing in the repo asserted on it, which is why a test now does.
  • Changeset rewritten to describe user-facing impact rather than internals, per REVIEW.md.

Eight tests are added in normalize-and-validate-config.test.ts. Six failure cases each fail on main with the exact TypeError reported in the issue or found in review, one covers the corrected unsupported-key-type message, and one asserts a valid authorized_keys and trusted_user_ca_keys pair still validates clean, passing both before and after. These fields previously had no negative test coverage anywhere in the repo.


  • Tests
    • Tests included/updated
    • Automated tests not possible - manual testing has been completed as follows:
    • Additional testing not necessary because:
  • Public documentation
    • Cloudflare docs PR(s):
    • Documentation not necessary because: this only changes what wrangler does with a config that was already invalid. The documented shape of containers.authorized_keys, containers.trusted_user_ca_keys and containers.configuration is unchanged, and no valid config behaves differently.

…med input

Validating a `containers` entry pushed a type error into diagnostics and
then dereferenced the value it had just rejected. An `authorized_keys` or
`trusted_user_ca_keys` entry whose `public_key` was missing or was not a
string reached `key.public_key.toLowerCase()` and threw, and an entry that
was not an object threw earlier still, from the `in` operator inside
`hasProperty`. A `configuration` of `null` passed the `typeof !== "object"`
check and threw later from `Object.keys()`.

None of these are a `UserError`, so wrangler printed a stack trace and
asked the user to report a bug instead of naming the offending key.

Each check now gates the checks that depend on it. The two duplicated key
blocks become one shared `validateSshPublicKeys` helper, which also
rejects a non-object entry up front, and the `configuration` type check
gains the missing `null` case so `Object.keys` only sees a real object.
No existing error message changed.
Copilot AI lite review requested due to automatic review settings August 8, 2026 08:12
@changeset-bot

changeset-bot Bot commented Aug 8, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e622478

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 6 packages
Name Type
@cloudflare/workers-utils Patch
@cloudflare/autoconfig Patch
@cloudflare/cli-shared-helpers Patch
@cloudflare/deploy-helpers Patch
@cloudflare/remote-bindings Patch
@cloudflare/workers-auth Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@workers-devprod
workers-devprod requested review from a team and NuroDev and removed request for a team August 8, 2026 08:12
@workers-devprod

Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • @cloudflare/wrangler
Show detailed file reviewers
  • .changeset/container-key-validation-crash.md: [@cloudflare/wrangler]
  • packages/workers-utils/src/config/validation.ts: [@cloudflare/wrangler]
  • packages/workers-utils/tests/config/validation/normalize-and-validate-config.test.ts: [@cloudflare/wrangler]

devin-ai-integration[bot]

This comment was marked as resolved.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes @cloudflare/workers-utils config validation for containers so malformed authorized_keys, trusted_user_ca_keys, and configuration values produce diagnostics instead of throwing raw TypeErrors from normalizeAndValidateConfig() (improving Wrangler’s error handling for invalid configs).

Changes:

  • Adds a shared validateSshPublicKeys() helper to validate containers.authorized_keys and containers.trusted_user_ca_keys with proper gating (no dereference after a failed type check).
  • Treats containers.configuration: null as invalid and avoids running additional-properties validation unless it’s a real object.
  • Adds unit tests covering several previously-crashing malformed containers configurations.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
packages/workers-utils/src/config/validation.ts Refactors SSH key validation into a helper and tightens containers.configuration validation/guarding.
packages/workers-utils/tests/config/validation/normalize-and-validate-config.test.ts Adds regression tests ensuring malformed containers configs produce diagnostics instead of throwing.
.changeset/container-key-validation-crash.md Adds a patch changeset documenting the crash-to-diagnostics behavior fix.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 3484 to 3488
if (
typeof containerAppOptional.configuration !== "object" ||
containerAppOptional.configuration === null ||
Array.isArray(containerAppOptional.configuration)
) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems valid to me, could you address it? 🙏

Comment on lines +3405 to +3407
diagnostics.errors.push(
`${fieldPath}.public_key is a unsupported key type. Please provide a ED25519 public key.`
);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a valid comment, could you please fix this? 🙏

…uration

Review caught that the `configuration` type check reported a non-object
and then fell through to the limits-versus-instance_type check, which
dereferences `configuration.disk`, `.vcpu` and `.memory_mib`. A config
pairing `configuration: null` with `instance_type` therefore still threw
`TypeError: Cannot read properties of null (reading 'disk')`, which is
the exact class of crash this branch removes.

The limits check is now an `else` branch of the type check, so it only
runs once `configuration` is known to be a real object.

Also correct the article agreement in the unsupported key type message,
which read "a unsupported" and "a ED25519". No test or snapshot covered
that string, so a case for it is added alongside the regression test
pairing a null configuration with an instance type.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

@dario-piotrowicz dario-piotrowicz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR generally looks good to me but there seem to be a few things that need fixing

Could you have a look @Neal006? 🙏

});

describe("[containers]", () => {
it("should error if containers is not an object", ({ expect }) => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would be a much clearer place for the the should error if containers.configuration is null test , could you move it here? 🙏

Comment on lines +3405 to +3407
diagnostics.errors.push(
`${fieldPath}.public_key is a unsupported key type. Please provide a ED25519 public key.`
);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a valid comment, could you please fix this? 🙏

Comment on lines 3484 to 3488
if (
typeof containerAppOptional.configuration !== "object" ||
containerAppOptional.configuration === null ||
Array.isArray(containerAppOptional.configuration)
) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems valid to me, could you address it? 🙏

@pkg-pr-new

pkg-pr-new Bot commented Aug 11, 2026

Copy link
Copy Markdown
@cloudflare/autoconfig

npm i https://pkg.pr.new/@cloudflare/autoconfig@15088

@cloudflare/build-output-utils

npm i https://pkg.pr.new/@cloudflare/build-output-utils@15088

@cloudflare/config

npm i https://pkg.pr.new/@cloudflare/config@15088

create-cloudflare

npm i https://pkg.pr.new/create-cloudflare@15088

@cloudflare/deploy-helpers

npm i https://pkg.pr.new/@cloudflare/deploy-helpers@15088

@cloudflare/kv-asset-handler

npm i https://pkg.pr.new/@cloudflare/kv-asset-handler@15088

miniflare

npm i https://pkg.pr.new/miniflare@15088

@cloudflare/pages-functions

npm i https://pkg.pr.new/@cloudflare/pages-functions@15088

@cloudflare/pages-shared

npm i https://pkg.pr.new/@cloudflare/pages-shared@15088

@cloudflare/unenv-preset

npm i https://pkg.pr.new/@cloudflare/unenv-preset@15088

@cloudflare/vite-plugin

npm i https://pkg.pr.new/@cloudflare/vite-plugin@15088

@cloudflare/vitest-pool-workers

npm i https://pkg.pr.new/@cloudflare/vitest-pool-workers@15088

@cloudflare/workers-auth

npm i https://pkg.pr.new/@cloudflare/workers-auth@15088

@cloudflare/workers-editor-shared

npm i https://pkg.pr.new/@cloudflare/workers-editor-shared@15088

@cloudflare/workers-utils

npm i https://pkg.pr.new/@cloudflare/workers-utils@15088

wrangler

npm i https://pkg.pr.new/wrangler@15088

commit: 041f4e7

…r containers type checks

The two tests covering a null `containers.configuration`, one on its own
and one paired with an `instance_type`, sat further down among the SSH
key cases, which is where they happened to be written rather than where
they belong. Move both up next to the checks that assert on the shape of
the `containers` field, so every type error for the block reads in one
place.

No test bodies or assertions change.
@Neal006

Neal006 commented Aug 11, 2026

Copy link
Copy Markdown
Author

@dario-piotrowicz thanks for taking the time to go through this. All three points are now covered, and two of them were already handled in a follow-up commit that landed before your pass, so the older threads are pointing at code that has since changed. Summary in one place:

1. Test placement (your comment on normalize-and-validate-config.test.ts). Done in e622478. Both null configuration cases, should error if containers.configuration is null and should error if containers.configuration is null and instance_type is set, now sit directly after the containers shape checks at the top of the [containers] describe, so every type error for the block reads in one place. Pure move, no test body or assertion changed.

2. Grammar in the unsupported key type message. Already fixed in 041f4e7, pushed on 8 Aug. The diagnostic now reads is an unsupported key type. Please provide an ED25519 public key. and is covered by should error if an authorized_keys public_key is not an ED25519 key, which previously had no test at all.

3. configuration: null combined with instance_type. Also fixed in 041f4e7. The limits versus instance_type check is now an else if on the configuration type check, so .disk, .vcpu and .memory_mib are only read once configuration is known to be a real non-null object. The regression test named above pairs configuration: null with instance_type: "lite" and asserts the diagnostic instead of the crash.

Where the PR stands. Three root causes in validateContainerApp, all in the same class of bug: a type error is recorded into diagnostics and then the rejected value is dereferenced anyway. The two duplicated key validation blocks are one shared helper, so authorized_keys and trusted_user_ca_keys cannot drift apart again. Net removal of source lines, with tests for each previously crashing config.

Verification on the current head: workers-utils suite passes at 475 tests, tsc -p clean, oxfmt clean. The remaining red checks are in areas this change does not touch, the Windows fixtures one being @fixture/additional-modules > watches additional modules, a file watcher flake. Happy to rebase for a clean run if that helps.

Could you have another look when you get a moment, and let me know if anything else needs changing? Glad to make any further adjustments you would like.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Untriaged

Development

Successfully merging this pull request may close these issues.

[workers-utils] Malformed containers authorized_keys, trusted_user_ca_keys or configuration crashes config validation with a TypeError

4 participants