Merge branch 'mergeback/2.5.1' into 'develop'

mergeback: 2.5.1

See merge request pleroma/pleroma!3846
This commit is contained in:
tusooa 2023-03-02 02:25:11 +00:00
commit 714bf0cb23
75 changed files with 88 additions and 16 deletions

View File

@ -16,6 +16,24 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Removed ### Removed
## 2.5.1
### Added
- Allow customizing instance languages
### Fixed
- Security: uploading HTTP endpoint can no longer create directories in the upload dir (internal APIs, like backup, still can do it.)
- ~ character in urls in Markdown posts are handled properly
- Exiftool upload filter will now ignore SVG files
- Fix `block_from_stranger` setting
- Fix rel="me"
- Docker images will now run properly
- Fix inproper content being cached in report content
- Notification filter on object content will not operate on the ones that inherently have no content
- ZWNJ and double dots in links are parsed properly for Plain-text posts
- OTP releases will work on systems with a newer libcrypt
- Errors when running Exiftool.ReadDescription filter will not be filled into the image description
## 2.5.0 - 2022-12-23 ## 2.5.0 - 2022-12-23
### Removed ### Removed

View File

@ -1453,13 +1453,22 @@ def fetch_activities_bounded(
@spec upload(Upload.source(), keyword()) :: {:ok, Object.t()} | {:error, any()} @spec upload(Upload.source(), keyword()) :: {:ok, Object.t()} | {:error, any()}
def upload(file, opts \\ []) do def upload(file, opts \\ []) do
with {:ok, data} <- Upload.store(file, opts) do with {:ok, data} <- Upload.store(sanitize_upload_file(file), opts) do
obj_data = Maps.put_if_present(data, "actor", opts[:actor]) obj_data = Maps.put_if_present(data, "actor", opts[:actor])
Repo.insert(%Object{data: obj_data}) Repo.insert(%Object{data: obj_data})
end end
end end
defp sanitize_upload_file(%Plug.Upload{filename: filename} = upload) when is_binary(filename) do
%Plug.Upload{
upload
| filename: Path.basename(filename)
}
end
defp sanitize_upload_file(upload), do: upload
@spec get_actor_url(any()) :: binary() | nil @spec get_actor_url(any()) :: binary() | nil
defp get_actor_url(url) when is_binary(url), do: url defp get_actor_url(url) when is_binary(url), do: url
defp get_actor_url(%{"href" => href}) when is_binary(href), do: href defp get_actor_url(%{"href" => href}) when is_binary(href), do: href

View File

@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do
def project do def project do
[ [
app: :pleroma, app: :pleroma,
version: version("2.5.50"), version: version("2.5.51"),
elixir: "~> 1.11", elixir: "~> 1.11",
elixirc_paths: elixirc_paths(Mix.env()), elixirc_paths: elixirc_paths(Mix.env()),
compilers: [:phoenix, :gettext] ++ Mix.compilers(), compilers: [:phoenix, :gettext] ++ Mix.compilers(),

View File

@ -1 +1 @@
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,user-scalable=no"><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><script defer=defer src=/static/js/9169.335214f6ab57538eae0b.js></script><script defer=defer src=/static/js/app.4c23e08cf351a54f4177.js></script><link href=/static/css/app.86977512e08af1f17d78.css rel=stylesheet></head><body class=hidden><noscript>To use Pleroma, please enable JavaScript.</noscript><div id=app></div><div id=popovers></body></html> <!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,user-scalable=no"><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><script defer=defer src=/static/js/2724.e4840c73281069ba54ab.js></script><script defer=defer src=/static/js/app.8d2126d35dba9482db51.js></script><link href=/static/css/app.48e52505beba5b9ab69b.css rel=stylesheet></head><body class=hidden><noscript>To use Pleroma, please enable JavaScript.</noscript><div id=app></div><div id=modal></div><div id=popovers></body></html>

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
{"version":3,"file":"static/css/1325.715a7f40cdd53f460ef4.css","mappings":"AACA,uBAGE,mBAFA,aACA,YAEA,uBACA,4BACE,YACA,iBCPJ,gBACE,gBAEA,2DAEE,qBACA,iBACA,iEACE,mBAEF,mFACE,gBAKF,2CASE,8CAEA,yBAXF,2CAeI,yCAKN,sCAOE,YADA,eALA,gBACA,qBAEA,wBADA,uCAEA,YAEA,CAEA,yBATF,sCAWI,YADA,eACA,EAGF,kDACE,YACA,kBAEA,uDACE,eACA,eACA,cAKN,iCACE,aACA,mCACE,kBAGF,gDACE,aACA","sources":["webpack://pleroma_fe/./src/components/async_component_error/async_component_error.vue","webpack://pleroma_fe/./src/components/settings_modal/settings_modal.scss"],"sourcesContent":["\n.async-component-error {\n display: flex;\n height: 100%;\n align-items: center;\n justify-content: center;\n .btn {\n margin: .5em;\n padding: .5em 2em;\n }\n}\n","@import 'src/_variables.scss';\n.settings-modal {\n overflow: hidden;\n\n .setting-list,\n .option-list {\n list-style-type: none;\n padding-left: 2em;\n li {\n margin-bottom: 0.5em;\n }\n .suboptions {\n margin-top: 0.3em\n }\n }\n\n &.peek {\n .settings-modal-panel {\n /* Explanation:\n * Modal is positioned vertically centered.\n * 100vh - 100% = Distance between modal's top+bottom boundaries and screen\n * (100vh - 100%) / 2 = Distance between bottom (or top) boundary and screen\n * + 100% - we move modal completely off-screen, it's top boundary touches\n * bottom of the screen\n * - 50px - leaving tiny amount of space so that titlebar + tiny amount of modal is visible\n */\n transform: translateY(calc(((100vh - 100%) / 2 + 100%) - 50px));\n\n @media all and (max-width: 800px) {\n /* For mobile, the modal takes 100% of the available screen.\n This ensures the minimized modal is always 50px above the browser bottom bar regardless of whether or not it is visible.\n */\n transform: translateY(calc(100% - 50px));\n }\n }\n }\n\n .settings-modal-panel {\n overflow: hidden;\n transition: transform;\n transition-timing-function: ease-in-out;\n transition-duration: 300ms;\n width: 1000px;\n max-width: 90vw;\n height: 90vh;\n\n @media all and (max-width: 800px) {\n max-width: 100vw;\n height: 100%;\n }\n\n >.panel-body {\n height: 100%;\n overflow-y: hidden;\n\n .btn {\n min-height: 2em;\n min-width: 10em;\n padding: 0 2em;\n }\n }\n }\n\n .settings-footer {\n display: flex;\n >* {\n margin-right: 0.5em;\n }\n\n .extra-content {\n display: flex;\n flex-grow: 1;\n }\n }\n}\n"],"names":[],"sourceRoot":""}

View File

@ -0,0 +1 @@
{"version":3,"file":"static/css/159.1d523a00378ebd68c5b3.css","mappings":"AAGA,gBACE,WAEA,0BACE,iBAEA,kDACE,aACA,eACA,cAEA,2DACE,aACA,cAGA,YAFA,WACA,UACA,CAEA,+DACE,YAEA,qEACE","sources":["webpack://pleroma_fe/./src/components/sticker_picker/sticker_picker.vue"],"sourcesContent":["\n@import \"../../variables\";\n\n.sticker-picker {\n width: 100%;\n\n .contents {\n min-height: 250px;\n\n .sticker-picker-content {\n display: flex;\n flex-wrap: wrap;\n padding: 0 4px;\n\n .sticker {\n display: flex;\n flex: 1 1 auto;\n margin: 4px;\n width: 56px;\n height: 56px;\n\n img {\n height: 100%;\n\n &:hover {\n filter: drop-shadow(0 0 5px var(--accent, $fallback--link));\n }\n }\n }\n }\n }\n}\n\n"],"names":[],"sourceRoot":""}

Binary file not shown.

View File

@ -0,0 +1 @@
{"version":3,"file":"static/css/5948.06d2a0d84620cba6a4fb.css","mappings":"AACA,uBAGE,mBAFA,aACA,YAEA,uBAEA,4BACE,YACA,iBCPJ,gBACE,gBAEA,2DAEE,qBACA,iBAEA,iEACE,mBAGF,mFACE,gBAIJ,sCAOE,YADA,eALA,gBACA,qBAEA,wBADA,uCAEA,YAEA,CAEA,yBATF,sCAWI,YADA,eACA,EAGF,kDACE,YACA,kBAEA,uDACE,eACA,eACA,cAKN,iCACE,aAEA,mCACE,kBAGF,gDACE,aACA,YAKF,2CASE,8CAEA,yBAXF,2CAgBI","sources":["webpack://pleroma_fe/./src/components/async_component_error/async_component_error.vue","webpack://pleroma_fe/./src/components/settings_modal/settings_modal.scss"],"sourcesContent":["\n.async-component-error {\n display: flex;\n height: 100%;\n align-items: center;\n justify-content: center;\n\n .btn {\n margin: 0.5em;\n padding: 0.5em 2em;\n }\n}\n","@import \"src/variables\";\n\n.settings-modal {\n overflow: hidden;\n\n .setting-list,\n .option-list {\n list-style-type: none;\n padding-left: 2em;\n\n li {\n margin-bottom: 0.5em;\n }\n\n .suboptions {\n margin-top: 0.3em;\n }\n }\n\n .settings-modal-panel {\n overflow: hidden;\n transition: transform;\n transition-timing-function: ease-in-out;\n transition-duration: 300ms;\n width: 1000px;\n max-width: 90vw;\n height: 90vh;\n\n @media all and (max-width: 800px) {\n max-width: 100vw;\n height: 100%;\n }\n\n >.panel-body {\n height: 100%;\n overflow-y: hidden;\n\n .btn {\n min-height: 2em;\n min-width: 10em;\n padding: 0 2em;\n }\n }\n }\n\n .settings-footer {\n display: flex;\n\n >* {\n margin-right: 0.5em;\n }\n\n .extra-content {\n display: flex;\n flex-grow: 1;\n }\n }\n\n &.peek {\n .settings-modal-panel {\n /* Explanation:\n * Modal is positioned vertically centered.\n * 100vh - 100% = Distance between modal's top+bottom boundaries and screen\n * (100vh - 100%) / 2 = Distance between bottom (or top) boundary and screen\n * + 100% - we move modal completely off-screen, it's top boundary touches\n * bottom of the screen\n * - 50px - leaving tiny amount of space so that titlebar + tiny amount of modal is visible\n */\n transform: translateY(calc(((100vh - 100%) / 2 + 100%) - 50px));\n\n @media all and (max-width: 800px) {\n /* For mobile, the modal takes 100% of the available screen.\n This ensures the minimized modal is always 50px above the browser bottom\n bar regardless of whether or not it is visible.\n */\n transform: translateY(calc(100% - 50px));\n }\n }\n }\n}\n"],"names":[],"sourceRoot":""}

View File

@ -0,0 +1 @@
{"version":3,"file":"static/css/6464.169260b661120cc50815.css","mappings":"AAEA,oBACE,gBAGF,yBACE,mBAAoB,CACpB,sBAAuB,CACvB,oBAAqB,CAErB,eACA,kBACA,qBAEA,wBADA,sCACA,CAEA,+BACE,eACA,iBAGF,yBAhBF,yBAqBI,aAGF,0BAxBF,yBAyBI,cAGF,kCAGE,8CACA,4CAFA,wCADA,eAGA,CAGE,iDACE,oCAKN,qCAGE,gCADA,mBADA,oBAEA,CAGF,uCAGE,eACA,2BAFA,kBADA,UAGA,CAGF,sCAWE,gDAJA,YANA,qCACA,2CAUA,oBAHA,kBACA,kBAPA,+DAEA,wBADA,uCAEA,WAEA,UAIA,CAGF,qCACE,+BAGF,wCACE,kCAGF,2CAKE,6GACE,CADF,sGADA,gBAHA,qCAEA,wBADA,kCAIE,CAIJ,qCACE,iBAGF,+BAKE,uCAEA,4CACE,YAEA,0BADA,UACA,CAGF,iDACE","sources":["webpack://pleroma_fe/./src/components/update_notification/update_notification.scss"],"sourcesContent":["@import \"src/variables\";\n\n.UpdateNotification {\n overflow: hidden;\n}\n\n.UpdateNotificationModal {\n --__top-fringe: 15em; // how much pleroma-tan should stick her head above\n --__bottom-fringe: 80em; // just reserving as much as we can, number is mostly irrelevant\n --__right-fringe: 8em;\n\n font-size: 15px;\n position: relative;\n transition: transform;\n transition-timing-function: ease-in-out;\n transition-duration: 500ms;\n\n .text {\n max-width: 40em;\n padding-left: 1em;\n }\n\n @media all and (max-width: 800px) {\n /* For mobile, the modal takes 100% of the available screen.\n This ensures the minimized modal is always 50px above the browser\n bottom bar regardless of whether or not it is visible.\n */\n width: 100vw;\n }\n\n @media all and (max-height: 600px) {\n display: none;\n }\n\n .content {\n overflow: hidden;\n margin-top: calc(-1 * var(--__top-fringe));\n margin-bottom: calc(-1 * var(--__bottom-fringe));\n margin-right: calc(-1 * var(--__right-fringe));\n\n &.-noImage {\n .text {\n padding-right: var(--__right-fringe);\n }\n }\n }\n\n .panel-body {\n border-width: 0 0 1px;\n border-style: solid;\n border-color: var(--border, $fallback--border);\n }\n\n .panel-footer {\n z-index: 22;\n position: relative;\n border-width: 0;\n grid-template-columns: auto;\n }\n\n .pleroma-tan {\n object-fit: cover;\n object-position: top;\n transition: position, left, right, top, bottom, max-width, max-height;\n transition-timing-function: ease-in-out;\n transition-duration: 500ms;\n width: 25em;\n float: right;\n z-index: 20;\n position: relative;\n shape-margin: 0.5em;\n filter: drop-shadow(5px 5px 10px rgb(0 0 0 / 50%));\n pointer-events: none;\n }\n\n .spacer-top {\n min-height: var(--__top-fringe);\n }\n\n .spacer-bottom {\n min-height: var(--__bottom-fringe);\n }\n\n .extra-info-group {\n transition: max-height, padding, height;\n transition-timing-function: ease-in;\n transition-duration: 700ms;\n max-height: 70vh;\n mask:\n linear-gradient(to top, white, transparent) bottom/100% 2px no-repeat,\n linear-gradient(to top, white, white);\n }\n\n .art-credit {\n text-align: right;\n }\n\n &.-peek {\n /* Explanation:\n * 100vh - 100% = Distance between modal's top+bottom boundaries and screen\n * (100vh - 100%) / 2 = Distance between bottom (or top) boundary and screen\n */\n transform: translateY(calc(((100vh - 100%) / 2)));\n\n .pleroma-tan {\n float: right;\n z-index: 10;\n shape-image-threshold: 70%;\n }\n\n .extra-info-group {\n max-height: 0;\n }\n }\n}\n"],"names":[],"sourceRoot":""}

View File

@ -1 +0,0 @@
{"version":3,"file":"static/css/6464.2fa2e5f1fa93842c62b1.css","mappings":"AACA,oBACE,gBAGF,yBACE,mBAAoB,CACpB,sBAAuB,CACvB,oBAAqB,CAErB,eACA,kBACA,qBAEA,wBADA,sCACA,CAEA,+BACE,eACA,iBAGF,yBAhBF,yBAoBI,aAGF,0BAvBF,yBAwBI,cAGF,kCAGE,8CACA,4CAFA,wCADA,eAGA,CAGE,iDACE,oCAKN,qCAGE,gCADA,mBADA,oBAEA,CAGF,uCAGE,eACA,2BAFA,kBADA,UAGA,CAGF,sCAWE,gDAJA,YANA,qCACA,2CAUA,oBAHA,kBACA,kBAPA,+DAEA,wBADA,uCAEA,WAEA,UAIA,CAGF,qCACE,+BAGF,wCACE,kCAGF,2CAKE,6GACE,CADF,sGADA,gBAHA,qCAEA,wBADA,kCAIE,CAIJ,qCACE,iBAGF,+BAKE,uCAEA,4CACE,YAEA,yBADA,UACA,CAGF,iDACE","sources":["webpack://pleroma_fe/./src/components/update_notification/update_notification.scss"],"sourcesContent":["@import 'src/_variables.scss';\n.UpdateNotification {\n overflow: hidden;\n}\n\n.UpdateNotificationModal {\n --__top-fringe: 15em; // how much pleroma-tan should stick her head above\n --__bottom-fringe: 80em; // just reserving as much as we can, number is mostly irrelevant\n --__right-fringe: 8em;\n\n font-size: 15px;\n position: relative;\n transition: transform;\n transition-timing-function: ease-in-out;\n transition-duration: 500ms;\n\n .text {\n max-width: 40em;\n padding-left: 1em;\n }\n\n @media all and (max-width: 800px) {\n /* For mobile, the modal takes 100% of the available screen.\n This ensures the minimized modal is always 50px above the browser bottom bar regardless of whether or not it is visible.\n */\n width: 100vw;\n }\n\n @media all and (max-height: 600px) {\n display: none;\n }\n\n .content {\n overflow: hidden;\n margin-top: calc(-1 * var(--__top-fringe));\n margin-bottom: calc(-1 * var(--__bottom-fringe));\n margin-right: calc(-1 * var(--__right-fringe));\n\n &.-noImage {\n .text {\n padding-right: var(--__right-fringe);\n }\n }\n }\n\n .panel-body {\n border-width: 0 0 1px 0;\n border-style: solid;\n border-color: var(--border, $fallback--border);\n }\n\n .panel-footer {\n z-index: 22;\n position: relative;\n border-width: 0;\n grid-template-columns: auto;\n }\n\n .pleroma-tan {\n object-fit: cover;\n object-position: top;\n transition: position, left, right, top, bottom, max-width, max-height;\n transition-timing-function: ease-in-out;\n transition-duration: 500ms;\n width: 25em;\n float: right;\n z-index: 20;\n position: relative;\n shape-margin: 0.5em;\n filter: drop-shadow(5px 5px 10px rgba(0,0,0,0.5));\n pointer-events: none;\n }\n\n .spacer-top {\n min-height: var(--__top-fringe);\n }\n\n .spacer-bottom {\n min-height: var(--__bottom-fringe);\n }\n\n .extra-info-group {\n transition: max-height, padding, height;\n transition-timing-function: ease-in;\n transition-duration: 700ms;\n max-height: 70vh;\n mask:\n linear-gradient(to top, white, transparent) bottom/100% 2px no-repeat,\n linear-gradient(to top, white, white);\n }\n\n .art-credit {\n text-align: right;\n }\n\n &.-peek {\n /* Explanation:\n * 100vh - 100% = Distance between modal's top+bottom boundaries and screen\n * (100vh - 100%) / 2 = Distance between bottom (or top) boundary and screen\n */\n transform: translateY(calc(((100vh - 100%) / 2)));\n\n .pleroma-tan {\n float: right;\n z-index: 10;\n shape-image-threshold: 0.7;\n }\n\n .extra-info-group {\n max-height: 0;\n }\n }\n}\n"],"names":[],"sourceRoot":""}

View File

@ -1 +0,0 @@
{"version":3,"file":"static/css/8532.88b90ac86f3060a3144e.css","mappings":"AAGA,gBACE,WACA,0BACE,iBACA,kDACE,aACA,eACA,cACA,2DACE,aACA,cAGA,YAFA,WACA,UACA,CACA,+DACE,YACA,qEACE","sources":["webpack://pleroma_fe/./src/components/sticker_picker/sticker_picker.vue"],"sourcesContent":["\n@import '../../_variables.scss';\n\n.sticker-picker {\n width: 100%;\n .contents {\n min-height: 250px;\n .sticker-picker-content {\n display: flex;\n flex-wrap: wrap;\n padding: 0 4px;\n .sticker {\n display: flex;\n flex: 1 1 auto;\n margin: 4px;\n width: 56px;\n height: 56px;\n img {\n height: 100%;\n &:hover {\n filter: drop-shadow(0 0 5px var(--accent, $fallback--link));\n }\n }\n }\n }\n }\n}\n\n"],"names":[],"sourceRoot":""}

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -33,12 +33,6 @@
/*! (c) Andrea Giammarchi - ISC */ /*! (c) Andrea Giammarchi - ISC */
/*! https://mths.be/punycode v1.3.2 by @mathias */
/*! js-cookie v3.0.1 | MIT */ /*! js-cookie v3.0.1 | MIT */
/*! lozad.js - v1.16.0 - 2020-09-06
* https://github.com/ApoorvSaxena/lozad.js
* Copyright (c) 2020 Apoorv Saxena; Licensed MIT */
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -25,6 +25,4 @@
* MIT Licensed * MIT Licensed
*/ */
/*! https://mths.be/punycode v1.3.2 by @mathias */
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */

Binary file not shown.

View File

@ -1342,6 +1342,14 @@ test "returns reblogs for users for whom reblogs have not been muted" do
%{test_file: test_file} %{test_file: test_file}
end end
test "strips / from filename", %{test_file: file} do
file = %Plug.Upload{file | filename: "../../../../../nested/bad.jpg"}
{:ok, %Object{} = object} = ActivityPub.upload(file)
[%{"href" => href}] = object.data["url"]
assert Regex.match?(~r"/bad.jpg$", href)
refute Regex.match?(~r"/nested/", href)
end
test "sets a description if given", %{test_file: file} do test "sets a description if given", %{test_file: file} do
{:ok, %Object{} = object} = ActivityPub.upload(file, description: "a cool file") {:ok, %Object{} = object} = ActivityPub.upload(file, description: "a cool file")
assert object.data["name"] == "a cool file" assert object.data["name"] == "a cool file"

View File

@ -122,6 +122,23 @@ test "/api/v2/media, upload_limit", %{conn: conn, user: user} do
assert :ok == File.rm(Path.absname("test/tmp/large_binary.data")) assert :ok == File.rm(Path.absname("test/tmp/large_binary.data"))
end end
test "Do not allow nested filename", %{conn: conn, image: image} do
image = %Plug.Upload{
image
| filename: "../../../../../nested/file.jpg"
}
desc = "Description of the image"
media =
conn
|> put_req_header("content-type", "multipart/form-data")
|> post("/api/v1/media", %{"file" => image, "description" => desc})
|> json_response_and_validate_schema(:ok)
refute Regex.match?(~r"/nested/", media["url"])
end
end end
describe "Update media description" do describe "Update media description" do

View File

@ -385,6 +385,34 @@ test "updates the user's background, upload_limit, returns a HTTP 413", %{
assert :ok == File.rm(Path.absname("test/tmp/large_binary.data")) assert :ok == File.rm(Path.absname("test/tmp/large_binary.data"))
end end
test "Strip / from upload files", %{user: user, conn: conn} do
new_image = %Plug.Upload{
content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "../../../../nested/an_image.jpg"
}
assert user.avatar == %{}
res =
patch(conn, "/api/v1/accounts/update_credentials", %{
"avatar" => new_image,
"header" => new_image,
"pleroma_background_image" => new_image
})
assert user_response = json_response_and_validate_schema(res, 200)
assert user_response["avatar"]
assert user_response["header"]
assert user_response["pleroma"]["background_image"]
refute Regex.match?(~r"/nested/", user_response["avatar"])
refute Regex.match?(~r"/nested/", user_response["header"])
refute Regex.match?(~r"/nested/", user_response["pleroma"]["background_image"])
user = User.get_by_id(user.id)
refute user.avatar == %{}
end
test "requires 'write:accounts' permission" do test "requires 'write:accounts' permission" do
token1 = insert(:oauth_token, scopes: ["read"]) token1 = insert(:oauth_token, scopes: ["read"])
token2 = insert(:oauth_token, scopes: ["write", "follow"]) token2 = insert(:oauth_token, scopes: ["write", "follow"])