Harden CIMD client resolution and caching
Tighten redirect URI validation for metadata documents, honor Cache-Control no-store when caching fetched documents, and resolve clients on the same transaction as authorization. Load external_client_id from the database and parse unbounded max-stale directives in cachecontrol. Signed-off-by: Bryan Frimin <bryan@probo.com>
This commit is contained in:
@@ -137,6 +137,11 @@ func ParseRequest(header string) (*RequestDirective, error) {
|
||||
|
||||
dir.maxAge = &seconds
|
||||
case MaxStale:
|
||||
if token.Value == "" {
|
||||
dir.maxStaleUnbounded = true
|
||||
break
|
||||
}
|
||||
|
||||
seconds, err := parseDeltaSeconds(token.Value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse max-stale: %w", err)
|
||||
@@ -292,6 +297,10 @@ func parseDirectives(header string, parse func(string) (*TokenPair, error)) ([]*
|
||||
tokens = append(tokens, token)
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("cannot scan cache-control directives: %w", err)
|
||||
}
|
||||
|
||||
return tokens, nil
|
||||
}
|
||||
|
||||
@@ -370,6 +379,7 @@ func scanCommaSeparatedWords(data []byte, atEOF bool) (advance int, token []byte
|
||||
|
||||
for width := 0; start < len(data); start += width {
|
||||
var r rune
|
||||
|
||||
r, width = utf8.DecodeRune(data[start:])
|
||||
if !isSpace(r) {
|
||||
break
|
||||
@@ -377,10 +387,12 @@ func scanCommaSeparatedWords(data []byte, atEOF bool) (advance int, token []byte
|
||||
}
|
||||
|
||||
var ws int
|
||||
|
||||
inQuotes := false
|
||||
|
||||
for width, i := 0, start; i < len(data); i += width {
|
||||
var r rune
|
||||
|
||||
r, width = utf8.DecodeRune(data[i:])
|
||||
|
||||
switch {
|
||||
|
||||
@@ -172,6 +172,37 @@ func TestParseRequest(t *testing.T) {
|
||||
require.Error(t, err)
|
||||
},
|
||||
)
|
||||
|
||||
t.Run(
|
||||
"max-stale without value",
|
||||
func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dir, err := cachecontrol.ParseRequest("max-stale")
|
||||
require.NoError(t, err)
|
||||
assert.True(t, dir.MaxStaleUnbounded())
|
||||
|
||||
_, bounded, ok := dir.MaxStale()
|
||||
require.True(t, ok)
|
||||
assert.False(t, bounded)
|
||||
},
|
||||
)
|
||||
|
||||
t.Run(
|
||||
"max-stale with value",
|
||||
func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dir, err := cachecontrol.ParseRequest("max-stale=120")
|
||||
require.NoError(t, err)
|
||||
assert.False(t, dir.MaxStaleUnbounded())
|
||||
|
||||
seconds, bounded, ok := dir.MaxStale()
|
||||
require.True(t, ok)
|
||||
assert.True(t, bounded)
|
||||
assert.Equal(t, uint64(120), seconds)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestParseResponse(t *testing.T) {
|
||||
@@ -378,10 +409,12 @@ func TestResponseMaxAgeDuration(t *testing.T) {
|
||||
_, gotOK := dir.MaxAgeDuration()
|
||||
assert.False(t, gotOK)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
gotAge, gotOK := dir.MaxAgeDuration()
|
||||
assert.True(t, gotOK)
|
||||
assert.Equal(t, tt.wantAge, gotAge)
|
||||
|
||||
@@ -18,14 +18,15 @@ import "time"
|
||||
|
||||
type (
|
||||
RequestDirective struct {
|
||||
maxAge *uint64
|
||||
maxStale *uint64
|
||||
minFresh *uint64
|
||||
noCache bool
|
||||
noStore bool
|
||||
noTransform bool
|
||||
onlyIfCached bool
|
||||
extensions map[string]string
|
||||
maxAge *uint64
|
||||
maxStale *uint64
|
||||
maxStaleUnbounded bool
|
||||
minFresh *uint64
|
||||
noCache bool
|
||||
noStore bool
|
||||
noTransform bool
|
||||
onlyIfCached bool
|
||||
extensions map[string]string
|
||||
}
|
||||
|
||||
ResponseDirective struct {
|
||||
@@ -50,12 +51,20 @@ func (d *RequestDirective) MaxAge() (uint64, bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func (d *RequestDirective) MaxStale() (uint64, bool) {
|
||||
if v := d.maxStale; v != nil {
|
||||
return *v, true
|
||||
func (d *RequestDirective) MaxStale() (seconds uint64, bounded bool, ok bool) {
|
||||
if d.maxStaleUnbounded {
|
||||
return 0, false, true
|
||||
}
|
||||
|
||||
return 0, false
|
||||
if v := d.maxStale; v != nil {
|
||||
return *v, true, true
|
||||
}
|
||||
|
||||
return 0, false, false
|
||||
}
|
||||
|
||||
func (d *RequestDirective) MaxStaleUnbounded() bool {
|
||||
return d.maxStaleUnbounded
|
||||
}
|
||||
|
||||
func (d *RequestDirective) MinFresh() (uint64, bool) {
|
||||
|
||||
Reference in New Issue
Block a user