Skip to content

Commit 4421a78

Browse files
authored
Merge pull request #31 from signalsciences/content-type-inspection
Improve Content-Type inspection
2 parents 5b809ec + 6ec0b13 commit 4421a78

File tree

9 files changed

+74
-5
lines changed

9 files changed

+74
-5
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# Golang Module Release Notes
22

3-
## Unreleased
3+
## 1.11.0 2022-01-18
44

5+
* Improved `Content-Type` header inspection
56
* Standardized release notes
67

78
## 1.10.0 2021-05-26

LICENSE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
The MIT License (MIT)
44

5-
Copyright (c) 2019-2020 Signal Sciences Corp.
5+
Copyright (c) 2019-2022 Signal Sciences Corp.
66

77
Permission is hereby granted, free of charge, to any person obtaining a copy
88
of this software and associated documentation files (the "Software"), to deal

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.10.0
1+
1.11.0

config.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net/http"
88
"path/filepath"
99
"runtime"
10+
"strings"
1011
"time"
1112
)
1213

@@ -45,6 +46,7 @@ type ModuleConfig struct {
4546
allowUnknownContentLength bool
4647
anomalyDuration time.Duration
4748
anomalySize int64
49+
expectedContentTypes []string
4850
debug bool
4951
headerExtractor HeaderExtractorFunc
5052
inspector Inspector
@@ -65,6 +67,7 @@ func NewModuleConfig(options ...ModuleConfigOption) (*ModuleConfig, error) {
6567
allowUnknownContentLength: DefaultAllowUnknownContentLength,
6668
anomalyDuration: DefaultAnomalyDuration,
6769
anomalySize: DefaultAnomalySize,
70+
expectedContentTypes: make([]string, 0),
6871
debug: DefaultDebug,
6972
headerExtractor: nil,
7073
inspector: DefaultInspector,
@@ -108,6 +111,17 @@ func (c *ModuleConfig) IsAllowCode(code int) bool {
108111
return code == 200
109112
}
110113

114+
// IsExpectedContentType returns true if the given content type string is
115+
// in the list of configured custom Content-Types
116+
func (c *ModuleConfig) IsExpectedContentType(s string) bool {
117+
for _, ct := range c.expectedContentTypes {
118+
if strings.HasPrefix(s, ct) {
119+
return true
120+
}
121+
}
122+
return false
123+
}
124+
111125
// AllowUnknownContentLength returns the configuration value
112126
func (c *ModuleConfig) AllowUnknownContentLength() bool {
113127
return c.allowUnknownContentLength
@@ -134,6 +148,11 @@ func (c *ModuleConfig) AnomalySize() int64 {
134148
return c.anomalySize
135149
}
136150

151+
// ExpectedContentTypes returns the slice of additional custom content types
152+
func (c *ModuleConfig) ExpectedContentTypes() []string {
153+
return c.expectedContentTypes
154+
}
155+
137156
// Debug returns the configuration value
138157
func (c *ModuleConfig) Debug() bool {
139158
return c.debug
@@ -250,6 +269,15 @@ func AnomalySize(size int64) ModuleConfigOption {
250269
}
251270
}
252271

272+
// ExpectedContentType is a function argument that adds a custom Content-Type
273+
// that should have request bodies sent to the agent for inspection
274+
func ExpectedContentType(s string) ModuleConfigOption {
275+
return func(c *ModuleConfig) error {
276+
c.expectedContentTypes = append(c.expectedContentTypes, s)
277+
return nil
278+
}
279+
}
280+
253281
// CustomInspector is a function argument that sets a custom inspector,
254282
// an optional inspector initializer to decide if inspection should occur, and
255283
// an optional inspector finalizer that can perform any post-inspection steps

config_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ func TestDefaultModuleConfig(t *testing.T) {
2323
if c.AnomalySize() != DefaultAnomalySize {
2424
t.Errorf("Unexpected AnomalySize: %v", c.AnomalySize())
2525
}
26+
if len(c.ExpectedContentTypes()) != 0 {
27+
t.Errorf("Unexpected ExpectedContentTypes: expected length 0, got %d", len(c.ExpectedContentTypes()))
28+
}
2629
if c.Debug() != DefaultDebug {
2730
t.Errorf("Unexpected Debug: %v", c.Debug())
2831
}
@@ -88,6 +91,8 @@ func TestConfiguredModuleConfig(t *testing.T) {
8891
AnomalySize(8192),
8992
CustomInspector(&RPCInspector{}, func(_ *http.Request) bool { return true }, func(_ *http.Request) {}),
9093
CustomHeaderExtractor(func(_ *http.Request) (http.Header, error) { return nil, nil }),
94+
ExpectedContentType("application/foobar"),
95+
ExpectedContentType("application/fizzbuzz"),
9196
Debug(true),
9297
MaxContentLength(500000),
9398
Socket("tcp", "0.0.0.0:1234"),
@@ -108,6 +113,9 @@ func TestConfiguredModuleConfig(t *testing.T) {
108113
if c.AnomalySize() != 8192 {
109114
t.Errorf("Unexpected AnomalySize: %v", c.AnomalySize())
110115
}
116+
if len(c.ExpectedContentTypes()) != 2 || c.ExpectedContentTypes()[0] != "application/foobar" || c.ExpectedContentTypes()[1] != "application/fizzbuzz" {
117+
t.Errorf("Unexpected ExpectedContentTypes: %v", c.ExpectedContentTypes())
118+
}
111119
if c.Debug() != true {
112120
t.Errorf("Unexpected Debug: %v", c.Debug())
113121
}
@@ -171,6 +179,8 @@ func TestFromModuleConfig(t *testing.T) {
171179
AltResponseCodes(403),
172180
AnomalyDuration(10*time.Second),
173181
AnomalySize(8192),
182+
ExpectedContentType("application/foobar"),
183+
ExpectedContentType("application/fizzbuzz"),
174184
CustomInspector(&RPCInspector{}, func(_ *http.Request) bool { return true }, func(_ *http.Request) {}),
175185
CustomHeaderExtractor(func(_ *http.Request) (http.Header, error) { return nil, nil }),
176186
Debug(true),
@@ -200,6 +210,9 @@ func TestFromModuleConfig(t *testing.T) {
200210
if c.AnomalySize() != 8192 {
201211
t.Errorf("Unexpected AnomalySize: %v", c.AnomalySize())
202212
}
213+
if len(c.ExpectedContentTypes()) != 2 || c.ExpectedContentTypes()[0] != "application/foobar" || c.ExpectedContentTypes()[1] != "application/fizzbuzz" {
214+
t.Errorf("Unexpected ExpectedContentTypes: %v", c.ExpectedContentTypes())
215+
}
203216
if c.Debug() != true {
204217
t.Errorf("Unexpected Debug: %v", c.Debug())
205218
}

module.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,26 @@ func shouldReadBody(req *http.Request, m *Module) bool {
445445
}
446446

447447
// only read certain types of content
448-
return inspectableContentType(req.Header.Get("Content-Type"))
448+
if inspectableContentType(req.Header.Get("Content-Type")) {
449+
return true
450+
}
451+
452+
// read custom configured content type(s)
453+
if m.config.IsExpectedContentType(req.Header.Get("Content-Type")) {
454+
return true
455+
}
456+
457+
// read the body if there are multiple Content-Type headers
458+
if len(req.Header.Values("Content-Type")) > 1 {
459+
return true
460+
}
461+
462+
// Check for comma separated Content-Types
463+
if len(strings.SplitN(req.Header.Get("Content-Type"), ",", 2)) > 1 {
464+
return true
465+
}
466+
467+
return false
449468
}
450469

451470
// inspectableContentType returns true for an inspectable content type
@@ -477,6 +496,10 @@ func inspectableContentType(s string) bool {
477496
// GraphQL
478497
case strings.HasPrefix(s, "application/graphql"):
479498
return true
499+
500+
// No type provided
501+
case s == "":
502+
return true
480503
}
481504

482505
return false

module_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ func TestInspectableContentType(t *testing.T) {
271271
{true, "text/x-json"},
272272
{true, "application/javascript"},
273273
{true, "application/graphql"},
274+
{true, ""},
274275
{false, "octet/stream"},
275276
{false, "junk/yard"},
276277
}
@@ -364,6 +365,7 @@ func TestModule(t *testing.T) {
364365
w := httptest.NewRecorder()
365366
m.ServeHTTP(w, req)
366367
resp := w.Result()
368+
defer resp.Body.Close()
367369

368370
if dump, err := httputil.DumpRequest(req, true); err == nil {
369371
t.Log("SERVER REQUEST:\n" + string(dump))

responsewriter_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ func testResponseWriter(t *testing.T, w ResponseWriter, flusher bool) {
8787

8888
// Verify the response
8989
resp := recorder.Result()
90+
defer resp.Body.Close()
91+
9092
if resp.StatusCode != status {
9193
t.Errorf("Unexpected status code=%d, expected=%d", resp.StatusCode, status)
9294
}

version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
package sigsci
22

3-
const version = "1.10.0"
3+
const version = "1.11.0"

0 commit comments

Comments
 (0)