summaryrefslogtreecommitdiff
path: root/internal/api/util
diff options
context:
space:
mode:
authorLibravatar kim <89579420+NyaaaWhatsUpDoc@users.noreply.github.com>2024-02-14 11:13:38 +0000
committerLibravatar GitHub <noreply@github.com>2024-02-14 12:13:38 +0100
commit2bafd7daf542d985ee76d9079a30a602cb7be827 (patch)
tree8817fe6f202155d660d75c17cd78ff5dae3d4530 /internal/api/util
parent[feature] Add metrics for instance user count, statuses count and federating ... (diff)
downloadgotosocial-2bafd7daf542d985ee76d9079a30a602cb7be827.tar.xz
[bugfix] add stricter checks during all stages of dereferencing remote AS objects (#2639)
* add stricter checks during all stages of dereferencing remote AS objects * a comment
Diffstat (limited to 'internal/api/util')
-rw-r--r--internal/api/util/mime.go114
-rw-r--r--internal/api/util/mime_test.go75
2 files changed, 188 insertions, 1 deletions
diff --git a/internal/api/util/mime.go b/internal/api/util/mime.go
index ad1b405cd..455a84de9 100644
--- a/internal/api/util/mime.go
+++ b/internal/api/util/mime.go
@@ -17,6 +17,8 @@
package util
+import "strings"
+
const (
// Possible GoToSocial mimetypes.
AppJSON = `application/json`
@@ -24,7 +26,8 @@ const (
AppXMLXRD = `application/xrd+xml`
AppRSSXML = `application/rss+xml`
AppActivityJSON = `application/activity+json`
- AppActivityLDJSON = `application/ld+json; profile="https://www.w3.org/ns/activitystreams"`
+ appActivityLDJSON = `application/ld+json` // without profile
+ AppActivityLDJSON = appActivityLDJSON + `; profile="https://www.w3.org/ns/activitystreams"`
AppJRDJSON = `application/jrd+json` // https://www.rfc-editor.org/rfc/rfc7033#section-10.2
AppForm = `application/x-www-form-urlencoded`
MultipartForm = `multipart/form-data`
@@ -32,3 +35,112 @@ const (
TextHTML = `text/html`
TextCSS = `text/css`
)
+
+// JSONContentType returns whether is application/json(;charset=utf-8)? content-type.
+func JSONContentType(ct string) bool {
+ p := splitContentType(ct)
+ p, ok := isUTF8ContentType(p)
+ return ok && len(p) == 1 &&
+ p[0] == AppJSON
+}
+
+// JSONJRDContentType returns whether is application/(jrd+)?json(;charset=utf-8)? content-type.
+func JSONJRDContentType(ct string) bool {
+ p := splitContentType(ct)
+ p, ok := isUTF8ContentType(p)
+ return ok && len(p) == 1 &&
+ p[0] == AppJSON ||
+ p[0] == AppJRDJSON
+}
+
+// XMLContentType returns whether is application/xml(;charset=utf-8)? content-type.
+func XMLContentType(ct string) bool {
+ p := splitContentType(ct)
+ p, ok := isUTF8ContentType(p)
+ return ok && len(p) == 1 &&
+ p[0] == AppXML
+}
+
+// XMLXRDContentType returns whether is application/(xrd+)?xml(;charset=utf-8)? content-type.
+func XMLXRDContentType(ct string) bool {
+ p := splitContentType(ct)
+ p, ok := isUTF8ContentType(p)
+ return ok && len(p) == 1 &&
+ p[0] == AppXML ||
+ p[0] == AppXMLXRD
+}
+
+// ASContentType returns whether is valid ActivityStreams content-types:
+// - application/activity+json
+// - application/ld+json;profile=https://w3.org/ns/activitystreams
+func ASContentType(ct string) bool {
+ p := splitContentType(ct)
+ p, ok := isUTF8ContentType(p)
+ if !ok {
+ return false
+ }
+ switch len(p) {
+ case 1:
+ return p[0] == AppActivityJSON
+ case 2:
+ return p[0] == appActivityLDJSON &&
+ p[1] == "profile=https://www.w3.org/ns/activitystreams" ||
+ p[1] == "profile=\"https://www.w3.org/ns/activitystreams\""
+ default:
+ return false
+ }
+}
+
+// NodeInfo2ContentType returns whether is nodeinfo schema 2.0 content-type.
+func NodeInfo2ContentType(ct string) bool {
+ p := splitContentType(ct)
+ p, ok := isUTF8ContentType(p)
+ if !ok {
+ return false
+ }
+ switch len(p) {
+ case 1:
+ return p[0] == AppJSON
+ case 2:
+ return p[0] == AppJSON &&
+ p[1] == "profile=\"http://nodeinfo.diaspora.software/ns/schema/2.0#\"" ||
+ p[1] == "profile=http://nodeinfo.diaspora.software/ns/schema/2.0#"
+ default:
+ return false
+ }
+}
+
+// isUTF8ContentType checks for a provided charset in given
+// type parts list, removes it and returns whether is utf-8.
+func isUTF8ContentType(p []string) ([]string, bool) {
+ const charset = "charset="
+ const charsetUTF8 = charset + "utf-8"
+ for i, part := range p {
+
+ // Only handle charset slice parts.
+ if part[:len(charset)] == charset {
+
+ // Check if is UTF-8 charset.
+ ok := (part == charsetUTF8)
+
+ // Drop this slice part.
+ _ = copy(p[i:], p[i+1:])
+ p = p[:len(p)-1]
+
+ return p, ok
+ }
+ }
+ return p, true
+}
+
+// splitContentType splits content-type into semi-colon
+// separated parts. useful when a charset is provided.
+// note this also maps all chars to their lowercase form.
+func splitContentType(ct string) []string {
+ s := strings.Split(ct, ";")
+ for i := range s {
+ s[i] = strings.TrimSpace(s[i])
+ s[i] = strings.ToLower(s[i])
+ }
+ return s
+}
diff --git a/internal/api/util/mime_test.go b/internal/api/util/mime_test.go
new file mode 100644
index 000000000..6b12d1436
--- /dev/null
+++ b/internal/api/util/mime_test.go
@@ -0,0 +1,75 @@
+package util_test
+
+import (
+ "testing"
+
+ "github.com/superseriousbusiness/gotosocial/internal/api/util"
+)
+
+func TestIsASContentType(t *testing.T) {
+ for _, test := range []struct {
+ Input string
+ Expect bool
+ }{
+ {
+ Input: "application/activity+json",
+ Expect: true,
+ },
+ {
+ Input: "application/activity+json; charset=utf-8",
+ Expect: true,
+ },
+ {
+ Input: "application/activity+json;charset=utf-8",
+ Expect: true,
+ },
+ {
+ Input: "application/activity+json ;charset=utf-8",
+ Expect: true,
+ },
+ {
+ Input: "application/activity+json ; charset=utf-8",
+ Expect: true,
+ },
+ {
+ Input: "application/ld+json;profile=https://www.w3.org/ns/activitystreams",
+ Expect: true,
+ },
+ {
+ Input: "application/ld+json;profile=\"https://www.w3.org/ns/activitystreams\"",
+ Expect: true,
+ },
+ {
+ Input: "application/ld+json ;profile=https://www.w3.org/ns/activitystreams",
+ Expect: true,
+ },
+ {
+ Input: "application/ld+json ;profile=\"https://www.w3.org/ns/activitystreams\"",
+ Expect: true,
+ },
+ {
+ Input: "application/ld+json ; profile=https://www.w3.org/ns/activitystreams",
+ Expect: true,
+ },
+ {
+ Input: "application/ld+json ; profile=\"https://www.w3.org/ns/activitystreams\"",
+ Expect: true,
+ },
+ {
+ Input: "application/ld+json; profile=https://www.w3.org/ns/activitystreams",
+ Expect: true,
+ },
+ {
+ Input: "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"",
+ Expect: true,
+ },
+ {
+ Input: "application/ld+json",
+ Expect: false,
+ },
+ } {
+ if util.ASContentType(test.Input) != test.Expect {
+ t.Errorf("did not get expected result %v for input: %s", test.Expect, test.Input)
+ }
+ }
+}