Skip to content

api/pkg/authconfig: reject multiple JSON documents in Decode#51919

Merged
vvoland merged 1 commit intomoby:masterfrom
htoyoda18:fix/decode
Jan 26, 2026
Merged

api/pkg/authconfig: reject multiple JSON documents in Decode#51919
vvoland merged 1 commit intomoby:masterfrom
htoyoda18:fix/decode

Conversation

@htoyoda18
Copy link
Contributor

@htoyoda18 htoyoda18 commented Jan 24, 2026

This fixes an issue where multiple JSON documents in the authentication payload would be silently ignored. The decoder now explicitly rejects any trailing data after the first valid JSON document.

- What I did
Fixed an issue in the authconfig package where multiple JSON documents in authentication payloads were silently ignored.

The decode() function now explicitly validates that only a single JSON document is present and rejects any trailing data.
- How I did it

  • Modified the decode() function to perform an additional Decode() call after successfully parsing the first JSON document
  • Check if the second decode returns io.EOF (expected) or finds additional data (error)
  • Used json.RawMessage instead of any for efficient existence checking without full parsing of potentially large trailing data
  • Updated the existing test case that had a FIXME comment indicating this behavior should not be accepted
  • Added three new test cases:
    • JSON with trailing whitespace (should accept)
    • JSON with trailing invalid data (should reject)
    • Multiple empty JSON documents (should reject)

- How to verify it
Run the test suite:

cd api/pkg/authconfig
go test -v

All tests pass, including the new test cases that verify multiple JSON documents are properly rejected.
Benchmark results show minimal performance impact:

go test -bench=BenchmarkDecodeAuthConfig -benchmem

- Human readable description for the release notes

Explicitly reject multiple `AuthConfig` values being passed instead of ignoring them silently.

- A picture of a cute animal (not mandatory but encouraged)
image

Comment on lines 72 to 93
func decode(r io.Reader) (*registry.AuthConfig, error) {
authConfig := &registry.AuthConfig{}
if err := json.NewDecoder(r).Decode(authConfig); err != nil {
decoder := json.NewDecoder(r)
if err := decoder.Decode(authConfig); err != nil {
// always return an (empty) AuthConfig to increase compatibility with
// the existing API.
return &registry.AuthConfig{}, invalid(fmt.Errorf("invalid JSON: %w", err))
}

// Reject multiple JSON documents to prevent potential security issues
// where additional data could be silently ignored.
var extra json.RawMessage
if err := decoder.Decode(&extra); err == nil {
// Additional JSON document found - this should not be allowed
return &registry.AuthConfig{}, invalid(errors.New("multiple JSON documents not allowed"))
} else if !errors.Is(err, io.EOF) {
// Unexpected trailing data (not valid JSON, not EOF)
return &registry.AuthConfig{}, invalid(fmt.Errorf("unexpected trailing data: %w", err))
}

return authConfig, nil
}
Copy link
Member

Choose a reason for hiding this comment

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

This can be greatly simplified; decoder already has an option for this to check if more content is present;

func decode(r io.Reader) (*registry.AuthConfig, error) {
	authConfig := &registry.AuthConfig{}
	dec := json.NewDecoder(r)
	if err := dec.Decode(authConfig); err != nil {
		// always return an (empty) AuthConfig to increase compatibility with
		// the existing API.
		return &registry.AuthConfig{}, invalid(fmt.Errorf("invalid JSON: %w", err))
	}
	if dec.More() {
		return &registry.AuthConfig{}, invalid(errors.New("multiple JSON documents not allowed"))
	}
	return authConfig, nil
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for the excellent suggestion! Using Decoder.More() makes the code much simpler and more efficient.

I've updated the implementation as you suggested
a06d84e

All tests pass with the simplified implementation.

@htoyoda18 htoyoda18 requested a review from thaJeztah January 24, 2026 16:02
@vvoland vvoland added this to the 29.2.0 milestone Jan 26, 2026
@thaJeztah
Copy link
Member

Changes look good; we should squash the commits. Also I don't think there's a real security issue here; the code expects a single auth-header value, so is expected to use the first value; additional values can safely be ignore, but it's good to notify the user that the value is incorrect.

@thaJeztah
Copy link
Member

Did a quick rebase and squash, but looks like GitHub actions is unhappy right now (issue with Windows runners?)

Explicitly reject multiple JSON documents in authentication payloads
instead of silently ignoring them. This helps notify users when the
authentication payload is incorrectly formatted.

Signed-off-by: hiroto.toyoda <[email protected]>
@htoyoda18
Copy link
Contributor Author

Did a quick rebase and squash, but looks like GitHub actions is unhappy right now (issue with Windows runners?)

Sorry for the conflict! I was working on squashing the commits at the same time you were doing the rebase. My force push may have overwritten your changes.

Should I pull your version, or is the current state acceptable? Happy to defer to your rebased version if you prefer.

@thaJeztah
Copy link
Member

GitHub actions has some issues with their runners; I'll try restarting CI later (no need to push again to start it)

@thaJeztah
Copy link
Member

Changes look good; I think the only diff would be the commit-message, so no problem!

Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

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

LGTM

@vvoland vvoland merged commit 2d8289c into moby:master Jan 26, 2026
338 of 350 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants