[bugfix] Sanitize incoming PropertyValue fields (#2722)
This commit is contained in:
parent
0b35257312
commit
f487fc5d4b
|
@ -387,6 +387,12 @@ type WithName interface {
|
||||||
SetActivityStreamsName(vocab.ActivityStreamsNameProperty)
|
SetActivityStreamsName(vocab.ActivityStreamsNameProperty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithValue represents an activity with SchemaValueProperty
|
||||||
|
type WithValue interface {
|
||||||
|
GetSchemaValue() vocab.SchemaValueProperty
|
||||||
|
SetSchemaValue(vocab.SchemaValueProperty)
|
||||||
|
}
|
||||||
|
|
||||||
// WithImage represents an activity with ActivityStreamsImageProperty
|
// WithImage represents an activity with ActivityStreamsImageProperty
|
||||||
type WithImage interface {
|
type WithImage interface {
|
||||||
GetActivityStreamsImage() vocab.ActivityStreamsImageProperty
|
GetActivityStreamsImage() vocab.ActivityStreamsImageProperty
|
||||||
|
|
|
@ -80,6 +80,7 @@ func NormalizeIncomingActivity(activity pub.Activity, rawJSON map[string]interfa
|
||||||
if accountable, ok := ToAccountable(dataType); ok {
|
if accountable, ok := ToAccountable(dataType); ok {
|
||||||
// Normalize everything we can on the accountable.
|
// Normalize everything we can on the accountable.
|
||||||
NormalizeIncomingSummary(accountable, rawData)
|
NormalizeIncomingSummary(accountable, rawData)
|
||||||
|
NormalizeIncomingFields(accountable, rawData)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -257,6 +258,64 @@ func NormalizeIncomingSummary(item WithSummary, rawJSON map[string]interface{})
|
||||||
item.SetActivityStreamsSummary(summaryProp)
|
item.SetActivityStreamsSummary(summaryProp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NormalizeIncomingFields sanitizes any PropertyValue fields on the
|
||||||
|
// given WithAttachment interface, by removing html completely from
|
||||||
|
// the "name" field, and sanitizing dodgy HTML out of the "value" field.
|
||||||
|
func NormalizeIncomingFields(item WithAttachment, rawJSON map[string]interface{}) {
|
||||||
|
rawAttachments, ok := rawJSON["attachment"]
|
||||||
|
if !ok {
|
||||||
|
// No attachments in rawJSON.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to slice if not already,
|
||||||
|
// so we can iterate through it.
|
||||||
|
var attachments []interface{}
|
||||||
|
if attachments, ok = rawAttachments.([]interface{}); !ok {
|
||||||
|
attachments = []interface{}{rawAttachments}
|
||||||
|
}
|
||||||
|
|
||||||
|
attachmentProperty := item.GetActivityStreamsAttachment()
|
||||||
|
if attachmentProperty == nil {
|
||||||
|
// Nothing to do here.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if l := attachmentProperty.Len(); l == 0 || l != len(attachments) {
|
||||||
|
// Mismatch between item and
|
||||||
|
// JSON, can't normalize.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep an index of where we are in the iter;
|
||||||
|
// we need this so we can modify the correct
|
||||||
|
// attachment, in case of multiples.
|
||||||
|
i := -1
|
||||||
|
|
||||||
|
for iter := attachmentProperty.Begin(); iter != attachmentProperty.End(); iter = iter.Next() {
|
||||||
|
i++
|
||||||
|
|
||||||
|
if !iter.IsSchemaPropertyValue() {
|
||||||
|
// Not interested.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
pv := iter.GetSchemaPropertyValue()
|
||||||
|
if pv == nil {
|
||||||
|
// Odd.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
rawPv, ok := attachments[i].(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
NormalizeIncomingName(pv, rawPv)
|
||||||
|
NormalizeIncomingValue(pv, rawPv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NormalizeIncomingName replaces the Name of the given item
|
// NormalizeIncomingName replaces the Name of the given item
|
||||||
// with the raw 'name' value from the raw json object map.
|
// with the raw 'name' value from the raw json object map.
|
||||||
//
|
//
|
||||||
|
@ -289,6 +348,36 @@ func NormalizeIncomingName(item WithName, rawJSON map[string]interface{}) {
|
||||||
item.SetActivityStreamsName(nameProp)
|
item.SetActivityStreamsName(nameProp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NormalizeIncomingValue replaces the Value of the given
|
||||||
|
// tem with the raw 'value' from the raw json object map.
|
||||||
|
//
|
||||||
|
// noop if there was no name in the json object map or the
|
||||||
|
// value was not a plain string.
|
||||||
|
func NormalizeIncomingValue(item WithValue, rawJSON map[string]interface{}) {
|
||||||
|
rawValue, ok := rawJSON["value"]
|
||||||
|
if !ok {
|
||||||
|
// No value in rawJSON.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
value, ok := rawValue.(string)
|
||||||
|
if !ok {
|
||||||
|
// Not interested in non-string name.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value often contains links or
|
||||||
|
// mentions or other little snippets.
|
||||||
|
// Sanitize to HTML to allow these.
|
||||||
|
value = text.SanitizeToHTML(value)
|
||||||
|
|
||||||
|
// Set normalized name property from the raw string; this
|
||||||
|
// will replace any existing value property on the item.
|
||||||
|
valueProp := streams.NewSchemaValueProperty()
|
||||||
|
valueProp.Set(value)
|
||||||
|
item.SetSchemaValue(valueProp)
|
||||||
|
}
|
||||||
|
|
||||||
// NormalizeIncomingOneOf normalizes all oneOf (if any) of the given
|
// NormalizeIncomingOneOf normalizes all oneOf (if any) of the given
|
||||||
// item, replacing the 'name' field of each oneOf with the raw 'name'
|
// item, replacing the 'name' field of each oneOf with the raw 'name'
|
||||||
// value from the raw json object map, and doing sanitization
|
// value from the raw json object map, and doing sanitization
|
||||||
|
|
|
@ -177,6 +177,23 @@ func (suite *NormalizeTestSuite) getAccountable() (vocab.ActivityStreamsPerson,
|
||||||
"@context": "https://www.w3.org/ns/activitystreams",
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
"id": "https://example.org/users/someone",
|
"id": "https://example.org/users/someone",
|
||||||
"summary": "about: I'm a #Barbie #girl in a #Barbie #world\nLife in plastic, it's fantastic\nYou can brush my hair, undress me everywhere\nImagination, life is your creation\nI'm a blonde bimbo girl\nIn a fantasy world\nDress me up, make it tight\nI'm your dolly\nYou're my doll, rock and roll\nFeel the glamour in pink\nKiss me here, touch me there\nHanky panky",
|
"summary": "about: I'm a #Barbie #girl in a #Barbie #world\nLife in plastic, it's fantastic\nYou can brush my hair, undress me everywhere\nImagination, life is your creation\nI'm a blonde bimbo girl\nIn a fantasy world\nDress me up, make it tight\nI'm your dolly\nYou're my doll, rock and roll\nFeel the glamour in pink\nKiss me here, touch me there\nHanky panky",
|
||||||
|
"attachment": [
|
||||||
|
{
|
||||||
|
"name": "<strong>cheeky</strong>",
|
||||||
|
"type": "PropertyValue",
|
||||||
|
"value": "<script>alert(\"teehee!\")</script>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "buy me coffee?",
|
||||||
|
"type": "PropertyValue",
|
||||||
|
"value": "<a href=\"https://example.org/some_link_to_my_ko_fi\">Right here!</a>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "hello",
|
||||||
|
"type": "PropertyValue",
|
||||||
|
"value": "world"
|
||||||
|
}
|
||||||
|
],
|
||||||
"type": "Person"
|
"type": "Person"
|
||||||
}`)
|
}`)
|
||||||
|
|
||||||
|
@ -405,6 +422,38 @@ Kiss me here, touch me there
|
||||||
Hanky panky`, ap.ExtractSummary(accountable))
|
Hanky panky`, ap.ExtractSummary(accountable))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *NormalizeTestSuite) TestNormalizeAccountableFields() {
|
||||||
|
accountable, rawAccount := suite.getAccountable()
|
||||||
|
fields := ap.ExtractFields(accountable)
|
||||||
|
|
||||||
|
// Dodgy field.
|
||||||
|
suite.Equal(`<strong>cheeky</strong>`, fields[0].Name)
|
||||||
|
suite.Equal(`<script>alert("teehee!")</script>`, fields[0].Value)
|
||||||
|
|
||||||
|
// More or less OK field.
|
||||||
|
suite.Equal(`buy me coffee?`, fields[1].Name)
|
||||||
|
suite.Equal(`<a href="https://example.org/some_link_to_my_ko_fi">Right here!</a>`, fields[1].Value)
|
||||||
|
|
||||||
|
// Fine field.
|
||||||
|
suite.Equal(`hello`, fields[2].Name)
|
||||||
|
suite.Equal(`world`, fields[2].Value)
|
||||||
|
|
||||||
|
// Normalize 'em.
|
||||||
|
ap.NormalizeIncomingFields(accountable, rawAccount)
|
||||||
|
|
||||||
|
// Dodgy field should be removed.
|
||||||
|
fields = ap.ExtractFields(accountable)
|
||||||
|
suite.Len(fields, 2)
|
||||||
|
|
||||||
|
// More or less OK field is now very OK.
|
||||||
|
suite.Equal(`buy me coffee?`, fields[0].Name)
|
||||||
|
suite.Equal(`<a href="https://example.org/some_link_to_my_ko_fi" rel="nofollow noreferrer noopener" target="_blank">Right here!</a>`, fields[0].Value)
|
||||||
|
|
||||||
|
// Fine field continues to be fine.
|
||||||
|
suite.Equal(`hello`, fields[1].Name)
|
||||||
|
suite.Equal(`world`, fields[1].Value)
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *NormalizeTestSuite) TestNormalizeStatusableSummary() {
|
func (suite *NormalizeTestSuite) TestNormalizeStatusableSummary() {
|
||||||
statusable, rawAccount := suite.getStatusableWithWeirdSummaryAndName()
|
statusable, rawAccount := suite.getStatusableWithWeirdSummaryAndName()
|
||||||
suite.Equal(`warning: #WEIRD%20%23SUMMARY%20;;;;a;;a;asv%20%20%20%20khop8273987(*%5E&%5E)`, ap.ExtractSummary(statusable))
|
suite.Equal(`warning: #WEIRD%20%23SUMMARY%20;;;;a;;a;asv%20%20%20%20khop8273987(*%5E&%5E)`, ap.ExtractSummary(statusable))
|
||||||
|
|
Loading…
Reference in New Issue