CVE-2022-36009 (Go)

gomatrixserverlib is a Go library for matrix protocol federation. Dendrite is a Matrix homeserver written in Go, an alternative to Synapse. The power level parsing within gomatrixserverlib was failing to parse the `"events_default"` key of the `m.room.power_levels` event, defaulting the event default power level to zero in all cases. Power levels are the matrix terminology for user access level. In rooms where the `"events_default"` power level had been changed, this could result in events either being incorrectly authorised or rejected by Dendrite servers. gomatrixserverlib contains a fix as of commit `723fd49` and Dendrite 0.9.3 has been updated accordingly. Matrix rooms where the `"events_default"` power level has not been changed from the default of zero are not vulnerable. Users are advised to upgrade. There are no known workarounds for this issue.

Weakness:
CWE-863: name: Incorrect Authorization
description: The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check.

Repository: https://github.com/matrix-org/gomatrixserverlib

Vulnerable code:
file_path: eventcontent.go
snippet: func NewPowerLevelContentFromEvent(event *Event) (c PowerLevelContent, err error) {
	// Set the levels to their default values.
	c.Defaults()

	var strict bool
	if strict, err = event.roomVersion.RequireIntegerPowerLevels(); err != nil {
		return
	} else if strict {
		// Unmarshal directly to PowerLevelContent, since that will kick up an
		// error if one of the power levels isn't an int64.
		if err = json.Unmarshal(event.Content(), &c); err != nil {
			err = errorf("unparsable power_levels event content: %s", err.Error())
			return
		}
	} else {
		// We can't extract the JSON directly to the powerLevelContent because we
		// need to convert string values to int values.
		var content struct {
			InviteLevel        levelJSONValue            `json:"invite"`
			BanLevel           levelJSONValue            `json:"ban"`
			KickLevel          levelJSONValue            `json:"kick"`
			RedactLevel        levelJSONValue            `json:"redact"`
			UserLevels         map[string]levelJSONValue `json:"users"`
			UsersDefaultLevel  levelJSONValue            `json:"users_default"`
			EventLevels        map[string]levelJSONValue `json:"events"`
			StateDefaultLevel  levelJSONValue            `json:"state_default"`
			EventDefaultLevel  levelJSONValue            `json:"event_default"`
			NotificationLevels map[string]levelJSONValue `json:"notifications"`
		}
		if err = json.Unmarshal(event.Content(), &content); err != nil {
			err = errorf("unparsable power_levels event content: %s", err.Error())
			return
		}

		// Update the levels with the values that are present in the event content.
		content.InviteLevel.assignIfExists(&c.Invite)
		content.BanLevel.assignIfExists(&c.Ban)
		content.KickLevel.assignIfExists(&c.Kick)
		content.RedactLevel.assignIfExists(&c.Redact)
		content.UsersDefaultLevel.assignIfExists(&c.UsersDefault)
		content.StateDefaultLevel.assignIfExists(&c.StateDefault)
		content.EventDefaultLevel.assignIfExists(&c.EventsDefault)

		for k, v := range content.UserLevels {
			if c.Users == nil {
				c.Users = make(map[string]int64)
			}
			c.Users[k] = v.value
		}

		for k, v := range content.EventLevels {
			if c.Events == nil {
				c.Events = make(map[string]int64)
			}
			c.Events[k] = v.value
		}

		for k, v := range content.NotificationLevels {
			if c.Notifications == nil {
				c.Notifications = make(map[string]int64)
			}
			c.Notifications[k] = v.value
		}
	}

	return
}

Fixed code:
file_path: eventcontent.go
snippet: func NewPowerLevelContentFromEvent(event *Event) (c PowerLevelContent, err error) {
	// Set the levels to their default values.
	c.Defaults()

	var strict bool
	if strict, err = event.roomVersion.RequireIntegerPowerLevels(); err != nil {
		return
	} else if strict {
		// Unmarshal directly to PowerLevelContent, since that will kick up an
		// error if one of the power levels isn't an int64.
		if err = json.Unmarshal(event.Content(), &c); err != nil {
			err = errorf("unparsable power_levels event content: %s", err.Error())
			return
		}
	} else {
		// We can't extract the JSON directly to the powerLevelContent because we
		// need to convert string values to int values.
		var content struct {
			InviteLevel        levelJSONValue            `json:"invite"`
			BanLevel           levelJSONValue            `json:"ban"`
			KickLevel          levelJSONValue            `json:"kick"`
			RedactLevel        levelJSONValue            `json:"redact"`
			UserLevels         map[string]levelJSONValue `json:"users"`
			UsersDefaultLevel  levelJSONValue            `json:"users_default"`
			EventLevels        map[string]levelJSONValue `json:"events"`
			StateDefaultLevel  levelJSONValue            `json:"state_default"`
			EventDefaultLevel  levelJSONValue            `json:"events_default"`
			NotificationLevels map[string]levelJSONValue `json:"notifications"`
		}
		if err = json.Unmarshal(event.Content(), &content); err != nil {
			err = errorf("unparsable power_levels event content: %s", err.Error())
			return
		}

		// Update the levels with the values that are present in the event content.
		content.InviteLevel.assignIfExists(&c.Invite)
		content.BanLevel.assignIfExists(&c.Ban)
		content.KickLevel.assignIfExists(&c.Kick)
		content.RedactLevel.assignIfExists(&c.Redact)
		content.UsersDefaultLevel.assignIfExists(&c.UsersDefault)
		content.StateDefaultLevel.assignIfExists(&c.StateDefault)
		content.EventDefaultLevel.assignIfExists(&c.EventsDefault)

		for k, v := range content.UserLevels {
			if c.Users == nil {
				c.Users = make(map[string]int64)
			}
			c.Users[k] = v.value
		}

		for k, v := range content.EventLevels {
			if c.Events == nil {
				c.Events = make(map[string]int64)
			}
			c.Events[k] = v.value
		}

		for k, v := range content.NotificationLevels {
			if c.Notifications == nil {
				c.Notifications = make(map[string]int64)
			}
			c.Notifications[k] = v.value
		}
	}

	return
}