Prevents line breaks in delivery service remapText field (UI and API) (#4305) (#4317)
* prevents line breaks in ds.remapText (UI and API)
* adds test to ensure ds.remapText cannot include a line break
* updated fixtures to include valid line breaks for edge/mid header rewrites, regex remap and tr request/response headers
* adds period to GoDoc
* no need for ng-pattern
* textarea does not support pattern so switching back to ng-pattern
(cherry picked from commit c025d2297168fc46d4fd4a9693aad9161bdc0015)
diff --git a/lib/go-tc/deliveryservices.go b/lib/go-tc/deliveryservices.go
index 2042ae8..69a5de3 100644
--- a/lib/go-tc/deliveryservices.go
+++ b/lib/go-tc/deliveryservices.go
@@ -405,6 +405,7 @@
isDNSName := validation.NewStringRule(govalidator.IsDNSName, "must be a valid hostname")
noPeriods := validation.NewStringRule(tovalidate.NoPeriods, "cannot contain periods")
noSpaces := validation.NewStringRule(tovalidate.NoSpaces, "cannot contain spaces")
+ noLineBreaks := validation.NewStringRule(tovalidate.NoLineBreaks, "cannot contain line breaks")
errs := tovalidate.ToErrors(validation.Errors{
"active": validation.Validate(ds.Active, validation.NotNil),
"cdnId": validation.Validate(ds.CDNID, validation.Required),
@@ -415,6 +416,7 @@
"geoProvider": validation.Validate(ds.GeoProvider, validation.NotNil),
"logsEnabled": validation.Validate(ds.LogsEnabled, validation.NotNil),
"regionalGeoBlocking": validation.Validate(ds.RegionalGeoBlocking, validation.NotNil),
+ "remapText": validation.Validate(ds.RemapText, noLineBreaks),
"routingName": validation.Validate(ds.RoutingName, isDNSName, noPeriods, validation.Length(1, 48)),
"typeId": validation.Validate(ds.TypeID, validation.Required, validation.Min(1)),
"xmlId": validation.Validate(ds.XMLID, noSpaces, noPeriods, validation.Length(1, 48)),
diff --git a/lib/go-tc/tovalidate/rules.go b/lib/go-tc/tovalidate/rules.go
index f5d6622..e2f1555 100644
--- a/lib/go-tc/tovalidate/rules.go
+++ b/lib/go-tc/tovalidate/rules.go
@@ -28,6 +28,11 @@
return !strings.ContainsAny(str, " ")
}
+// NoLineBreaks returns true if the string has no line breaks.
+func NoLineBreaks(str string) bool {
+ return !strings.ContainsAny(str, "\n\r")
+}
+
// IsAlphanumericUnderscoreDash returns true if the string consists of only alphanumeric, underscore, or dash characters.
func IsAlphanumericUnderscoreDash(str string) bool {
return rxAlphanumericUnderscoreDash.MatchString(str)
diff --git a/traffic_ops/testing/api/v14/deliveryservices_test.go b/traffic_ops/testing/api/v14/deliveryservices_test.go
index c56ddf3..69e74b7 100644
--- a/traffic_ops/testing/api/v14/deliveryservices_test.go
+++ b/traffic_ops/testing/api/v14/deliveryservices_test.go
@@ -36,6 +36,7 @@
WithObjs(t, []TCObj{CDNs, Types, Tenants, Users, Parameters, Profiles, Statuses, Divisions, Regions, PhysLocations, CacheGroups, Servers, DeliveryServices}, func() {
UpdateTestDeliveryServices(t)
UpdateNullableTestDeliveryServices(t)
+ UpdateDeliveryServiceWithInvalidRemapText(t)
GetTestDeliveryServices(t)
DeliveryServiceMinorVersionsTest(t)
DeliveryServiceTenancyTest(t)
@@ -189,6 +190,39 @@
}
}
+// UpdateDeliveryServiceWithInvalidRemapText ensures that a delivery service can't be updated with a remap text value with a line break in it.
+func UpdateDeliveryServiceWithInvalidRemapText(t *testing.T) {
+ firstDS := testData.DeliveryServices[0]
+
+ dses, _, err := TOSession.GetDeliveryServicesNullable()
+ if err != nil {
+ t.Fatalf("cannot GET Delivery Services: %v", err)
+ }
+
+ remoteDS := tc.DeliveryServiceNullable{}
+ found := false
+ for _, ds := range dses {
+ if ds.XMLID == nil || ds.ID == nil {
+ continue
+ }
+ if *ds.XMLID == firstDS.XMLID {
+ found = true
+ remoteDS = ds
+ break
+ }
+ }
+ if !found {
+ t.Fatalf("GET Delivery Services missing: %v", firstDS.XMLID)
+ }
+
+ updatedRemapText := "@plugin=tslua.so @pparam=/opt/trafficserver/etc/trafficserver/remapPlugin1.lua\nline2"
+ remoteDS.RemapText = &updatedRemapText
+
+ if _, err := TOSession.UpdateDeliveryServiceNullable(strconv.Itoa(*remoteDS.ID), &remoteDS); err == nil {
+ t.Errorf("Delivery service updated with invalid remap text: %v", updatedRemapText)
+ }
+}
+
func DeleteTestDeliveryServices(t *testing.T) {
dses, _, err := TOSession.GetDeliveryServices()
if err != nil {
diff --git a/traffic_ops/testing/api/v14/tc-fixtures.json b/traffic_ops/testing/api/v14/tc-fixtures.json
index cb12363..c813a95 100644
--- a/traffic_ops/testing/api/v14/tc-fixtures.json
+++ b/traffic_ops/testing/api/v14/tc-fixtures.json
@@ -258,7 +258,7 @@
"dnsBypassIp6": "",
"dnsBypassTtl": 30,
"dscp": 40,
- "edgeHeaderRewrite": "edgeHeader1",
+ "edgeHeaderRewrite": "edgeHeader1\nedgeHeader2",
"exampleURLs": [
"http://ccr.ds1.example.net",
"https://ccr.ds1.example.net"
@@ -287,7 +287,7 @@
}
],
"maxDnsAnswers": 0,
- "midHeaderRewrite": "midHeader1",
+ "midHeaderRewrite": "midHeader1\nmidHeader2",
"missLat": 41.881944,
"missLong": -87.627778,
"multiSiteOrigin": false,
@@ -298,7 +298,7 @@
"protocol": 2,
"qstringIgnore": 1,
"rangeRequestHandling": 0,
- "regexRemap": "rr1",
+ "regexRemap": "rr1\nrr2",
"regionalGeoBlocking": false,
"remapText": "@plugin=tslua.so @pparam=/opt/trafficserver/etc/trafficserver/remapPlugin1.lua",
"routingName": "ccr-ds1",
@@ -325,7 +325,7 @@
"dnsBypassIp6": "",
"dnsBypassTtl": 30,
"dscp": 40,
- "edgeHeaderRewrite": "edgeRewrite2",
+ "edgeHeaderRewrite": "edgeRewrite1\nedgeHeader2",
"exampleURLs": [
"http://ccr.ds2.example.net",
"https://ccr.ds2x.example.net"
@@ -355,7 +355,7 @@
],
"maxDnsAnswers": 0,
"maxOriginConnections": -1,
- "midHeaderRewrite": "midRewrite2",
+ "midHeaderRewrite": "midHeader1\nmidHeader2",
"missLat": 41.881944,
"missLong": -87.627778,
"multiSiteOrigin": false,
@@ -366,7 +366,7 @@
"protocol": 2,
"qstringIgnore": 1,
"rangeRequestHandling": 0,
- "regexRemap": "regexRemap2",
+ "regexRemap": "rr1\nrr2",
"regionalGeoBlocking": false,
"remapText": "@plugin=tslua.so @pparam=/opt/trafficserver/etc/trafficserver/ds2plugin.lua",
"routingName": "ccr-ds2",
@@ -393,7 +393,7 @@
"dnsBypassIp6": "",
"dnsBypassTtl": 30,
"dscp": 40,
- "edgeHeaderRewrite": "edgeRewrite3",
+ "edgeHeaderRewrite": "edgeRewrite1\nedgeHeader2",
"exampleURLs": [
"http://ccr.ds3.example.net",
"https://ccr.ds3x.example.net"
@@ -423,7 +423,7 @@
],
"maxDnsAnswers": 0,
"maxOriginConnections": 0,
- "midHeaderRewrite": "midRewrite3",
+ "midHeaderRewrite": "midHeader1\nmidHeader2",
"missLat": 41.881944,
"missLong": -87.627778,
"multiSiteOrigin": false,
@@ -434,7 +434,7 @@
"protocol": 2,
"qstringIgnore": 1,
"rangeRequestHandling": 0,
- "regexRemap": "regexRemap3",
+ "regexRemap": "rr1\nrr2",
"regionalGeoBlocking": false,
"remapText": "@plugin=tslua.so @pparam=/opt/trafficserver/etc/trafficserver/ds3plugin.lua",
"routingName": "ccr-ds3",
@@ -520,7 +520,7 @@
"dnsBypassIp6": "",
"dnsBypassTtl": 30,
"dscp": 40,
- "edgeHeaderRewrite": "edgeHeader1",
+ "edgeHeaderRewrite": "edgeRewrite1\nedgeHeader2",
"fqPacingRate": 42,
"geoLimit": 0,
"geoLimitCountries": "",
@@ -538,7 +538,7 @@
"longDesc2": "ds1",
"maxDnsAnswers": 0,
"maxOriginConnections": 1000,
- "midHeaderRewrite": "midHeader1",
+ "midHeaderRewrite": "midHeader1\nmidHeader2",
"missLat": 41.881944,
"missLong": -87.627778,
"multiSiteOrigin": false,
@@ -549,7 +549,7 @@
"protocol": 2,
"qstringIgnore": 1,
"rangeRequestHandling": 0,
- "regexRemap": "rr1",
+ "regexRemap": "rr1\nrr2",
"regionalGeoBlocking": false,
"remapText": "@plugin=tslua.so @pparam=/opt/trafficserver/etc/trafficserver/remapPlugin1.lua",
"routingName": "cdn",
@@ -558,8 +558,8 @@
"sslKeyVersion": 2,
"tenantId": 1,
"tenant": "root",
- "trRequestHeaders": "X-Foo",
- "trResponseHeaders": "Access-Control-Allow-Origin: *",
+ "trRequestHeaders": "X-Foo\nX-Bar",
+ "trResponseHeaders": "Access-Control-Allow-Origin: *\nContent-Type: text/html; charset=utf-8",
"type": "HTTP_LIVE",
"xmlId": "ds-test-minor-versions",
"anonymousBlockingEnabled": true
@@ -578,7 +578,7 @@
"dnsBypassIp6": "",
"dnsBypassTtl": 30,
"dscp": 40,
- "edgeHeaderRewrite": "edgeHeader1",
+ "edgeHeaderRewrite": "edgeRewrite1\nedgeHeader2",
"exampleURLs": [
"http://ccr.msods1.example.net",
"https://ccr.msods1.example.net"
@@ -607,7 +607,7 @@
}
],
"maxDnsAnswers": 0,
- "midHeaderRewrite": "midHeader1",
+ "midHeaderRewrite": "midHeader1\nmidHeader2",
"missLat": 41.881944,
"missLong": -87.627778,
"multiSiteOrigin": true,
@@ -618,7 +618,7 @@
"protocol": 2,
"qstringIgnore": 1,
"rangeRequestHandling": 0,
- "regexRemap": "rr1",
+ "regexRemap": "rr1\nrr2",
"regionalGeoBlocking": false,
"remapText": "@plugin=tslua.so @pparam=/opt/trafficserver/etc/trafficserver/remapPlugin1.lua",
"routingName": "ccr-msods1",
@@ -645,7 +645,7 @@
"dnsBypassIp6": "",
"dnsBypassTtl": 30,
"dscp": 40,
- "edgeHeaderRewrite": "edgeHeader1",
+ "edgeHeaderRewrite": "edgeRewrite1\nedgeHeader2",
"exampleURLs": [
"http://ccr.ds1nat.example.net",
"https://ccr.ds1nat.example.net"
@@ -674,7 +674,7 @@
}
],
"maxDnsAnswers": 0,
- "midHeaderRewrite": "midHeader1",
+ "midHeaderRewrite": "midHeader1\nmidHeader2",
"missLat": 41.881944,
"missLong": -87.627778,
"multiSiteOrigin": false,
@@ -685,7 +685,7 @@
"protocol": 2,
"qstringIgnore": 1,
"rangeRequestHandling": 0,
- "regexRemap": "rr1",
+ "regexRemap": "rr1\nrr2",
"regionalGeoBlocking": false,
"remapText": "@plugin=tslua.so @pparam=/opt/trafficserver/etc/trafficserver/remapPlugin1.lua",
"routingName": "ccr-ds1nat",
diff --git a/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.DNS.tpl.html b/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.DNS.tpl.html
index 779e224..9b8a43b 100644
--- a/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.DNS.tpl.html
+++ b/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.DNS.tpl.html
@@ -534,7 +534,8 @@
</div>
</label>
<div class="col-md-10 col-sm-10 col-xs-12">
- <textarea id="remapText" name="remapText" class="form-control" ng-model="deliveryService.remapText" rows="3"></textarea>
+ <textarea id="remapText" name="remapText" class="form-control" ng-model="deliveryService.remapText" ng-pattern="/^[^\n\r]*$/" rows="3"></textarea>
+ <small class="input-error" ng-show="hasPropertyError(cacheConfig.remapText, 'pattern')">No Line Breaks</small>
<aside class="current-value" ng-if="settings.isRequest" ng-show="open() && deliveryService.remapText != dsCurrent.remapText">
<h3>Current Value</h3>
<pre>{{::dsCurrent.remapText}}</pre>
diff --git a/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.HTTP.tpl.html b/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.HTTP.tpl.html
index a491062..e0f1878 100644
--- a/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.HTTP.tpl.html
+++ b/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.HTTP.tpl.html
@@ -534,7 +534,8 @@
</div>
</label>
<div class="col-md-10 col-sm-10 col-xs-12">
- <textarea id="remapText" name="remapText" class="form-control" ng-model="deliveryService.remapText" rows="3"></textarea>
+ <textarea id="remapText" name="remapText" class="form-control" ng-model="deliveryService.remapText" ng-pattern="/^[^\n\r]*$/" rows="3"></textarea>
+ <small class="input-error" ng-show="hasPropertyError(cacheConfig.remapText, 'pattern')">No Line Breaks</small>
<aside class="current-value" ng-if="settings.isRequest" ng-show="open() && deliveryService.remapText != dsCurrent.remapText">
<h3>Current Value</h3>
<pre>{{::dsCurrent.remapText}}</pre>
diff --git a/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.anyMap.tpl.html b/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.anyMap.tpl.html
index 4db5f01..3c14f0d 100644
--- a/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.anyMap.tpl.html
+++ b/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.anyMap.tpl.html
@@ -273,7 +273,8 @@
</div>
</label>
<div class="col-md-10 col-sm-10 col-xs-12">
- <textarea id="remapText" name="remapText" class="form-control" ng-model="deliveryService.remapText" rows="3"></textarea>
+ <textarea id="remapText" name="remapText" class="form-control" ng-model="deliveryService.remapText" ng-pattern="/^[^\n\r]*$/" rows="3" required></textarea>
+ <small class="input-error" ng-show="hasPropertyError(cacheConfig.remapText, 'pattern')">No Line Breaks</small>
<aside class="current-value" ng-if="settings.isRequest" ng-show="open() && deliveryService.remapText != dsCurrent.remapText">
<h3>Current Value</h3>
<pre>{{::dsCurrent.remapText}}</pre>