Skip to content

Commit

Permalink
Check max length of notice key, other tweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
benhoyt committed Aug 28, 2023
1 parent 07be373 commit ec700d9
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 5 deletions.
5 changes: 5 additions & 0 deletions internals/daemon/api_notices.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import (
"github.com/canonical/pebble/internals/overlord/state"
)

const maxKeyLength = 255

// A very loose regex to ensure client keys are in the form "domain.com/key"
var clientKeyRegexp = regexp.MustCompile(`([a-z0-9-_]+\.)+[a-z0-9-_]+/.+`)

Expand Down Expand Up @@ -96,6 +98,9 @@ func v1PostNotices(c *Command, r *http.Request, _ *UserState) Response {
if !clientKeyRegexp.MatchString(payload.Key) {
return statusBadRequest(`invalid key %q (must be in "domain.com/key" format)`, payload.Key)
}
if len(payload.Key) > state.MaxNoticeKeyLength {
return statusBadRequest(`key too long (must be %d bytes or less)`, state.MaxNoticeKeyLength)
}

var repeatAfter time.Duration
if payload.RepeatAfter != "" {
Expand Down
13 changes: 8 additions & 5 deletions internals/overlord/state/notices.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@ package state

import (
"encoding/json"
"fmt"
"sort"
"strconv"
"time"

"github.com/canonical/pebble/internals/logger"
)

const MaxNoticeKeyLength = 255

// Notice represents an aggregated notice. The combination of Type and Key is unique.
type Notice struct {
// Server-generated unique ID for this notice (a surrogate key). Users
Expand Down Expand Up @@ -69,9 +72,9 @@ func (n *Notice) expired(now time.Time) bool {
return n.lastOccurred.Add(n.expireAfter).Before(now)
}

// jsonNotice exists so we can control how a Notice is JSON-marshalled. It
// jsonNotice exists so we can control how a Notice is marshalled to JSON. It
// needs to live in this package (rather than the API) because we save state
// to disk.
// to disk as JSON.
type jsonNotice struct {
ID string `json:"id"`
Type string `json:"type"`
Expand Down Expand Up @@ -122,13 +125,13 @@ func (n *Notice) UnmarshalJSON(data []byte) error {
if jn.RepeatAfter != "" {
n.repeatAfter, err = time.ParseDuration(jn.RepeatAfter)
if err != nil {
return err
return fmt.Errorf("invalid repeat-after duration: %w", err)
}
}
if jn.ExpireAfter != "" {
n.expireAfter, err = time.ParseDuration(jn.ExpireAfter)
if err != nil {
return err
return fmt.Errorf("invalid expire-after duration: %w", err)
}
}
return nil
Expand Down Expand Up @@ -166,7 +169,7 @@ func NoticeTypeFromString(s string) NoticeType {
// AddNotice adds an occurrence of a notice with the specified type and key
// and key-value data.
func (s *State) AddNotice(noticeType NoticeType, key string, data map[string]string, repeatAfter time.Duration) {
if noticeType == "" || key == "" {
if noticeType == "" || key == "" || len(key) > MaxNoticeKeyLength {
// Programming error
logger.Panicf("Internal error, please report: attempted to add invalid notice (type %q, key %q)",
noticeType, key)
Expand Down

0 comments on commit ec700d9

Please sign in to comment.