diff --git a/.gitignore b/.gitignore index d8e5ed553..355cea069 100644 --- a/.gitignore +++ b/.gitignore @@ -56,9 +56,6 @@ pleroma.iml # asdf .tool-versions -# mise -mise.toml - # Editor temp files *~ *# diff --git a/.woodpecker/changelog.yaml b/.woodpecker/changelog.yaml index 64062f17e..4f38ce618 100644 --- a/.woodpecker/changelog.yaml +++ b/.woodpecker/changelog.yaml @@ -1,19 +1,9 @@ when: - event: pull_request -labels: - platform: linux/amd64 - -variables: - script_file_entrypoint: &script_file_entrypoint - - /bin/sh - - -c - - 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh' - steps: check-changelog: image: docker.io/alpine:3.23 - entrypoint: *script_file_entrypoint commands: - apk add --no-cache git - sh ./tools/check-changelog diff --git a/.woodpecker/docker-combine.yaml b/.woodpecker/docker-combine.yaml deleted file mode 100644 index 6c91d26e9..000000000 --- a/.woodpecker/docker-combine.yaml +++ /dev/null @@ -1,60 +0,0 @@ -when: - - event: push - branch: ${CI_REPO_DEFAULT_BRANCH} - path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**", "Dockerfile" ] - - event: tag - - event: manual - branch: ${CI_REPO_DEFAULT_BRANCH} - - event: manual - branch: stable - -depends_on: - - docker - -skip_clone: true - -labels: - platform: linux/amd64 - -steps: - docker-develop-combine: - image: git.fluffytail.org/phnt/wpc-docker-tagger:latest - when: - - event: push - branch: ${CI_REPO_DEFAULT_BRANCH} - - event: manual - branch: ${CI_REPO_DEFAULT_BRANCH} - settings: &docker_settings - registry: "git.pleroma.social" - image: "pleroma/pleroma" - architectures: [amd64, arm64] - tags: - - latest - - develop - - ${CI_COMMIT_SHA:0:8} - username: - from_secret: pleroma-ci-user - password: - from_secret: pleroma-ci-password - - docker-stable-combine: - image: git.fluffytail.org/phnt/wpc-docker-tagger:latest - when: - - evaluate: 'CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG == ""' - settings: - <<: *docker_settings - tags: &stable_docker_tags - - latest - - stable - - ${CI_COMMIT_SHA:0:8} - - docker-stable-tag-combine: - image: git.fluffytail.org/phnt/wpc-docker-tagger:latest - when: - - event: tag - - evaluate: 'CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG != ""' - settings: - <<: *docker_settings - tags: - - <<: *stable_docker_tags - - ${CI_COMMIT_TAG} diff --git a/.woodpecker/docker.yaml b/.woodpecker/docker.yaml deleted file mode 100644 index abc6bfa3b..000000000 --- a/.woodpecker/docker.yaml +++ /dev/null @@ -1,96 +0,0 @@ -when: - - event: push - branch: ${CI_REPO_DEFAULT_BRANCH} - path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**", "Dockerfile" ] - - event: tag - - event: manual - branch: ${CI_REPO_DEFAULT_BRANCH} - - event: manual - branch: stable - -matrix: - platform: - - linux/amd64 - - linux/arm64 - -# This is needed for the when clauses below. -labels: - platform: ${platform} - memory: 'high' - -variables: - docker_variables: &docker_variables - repo: pleroma/pleroma - registry: git.pleroma.social - username: - from_secret: pleroma-ci-user - password: - from_secret: pleroma-ci-password - kaniko_image: &kaniko_image woodpeckerci/plugin-kaniko:2.3.1 - -steps: - docker-develop-amd64: - image: woodpeckerci/plugin-kaniko:2.3.1 - when: - - evaluate: 'platform == "linux/amd64" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' - settings: - <<: *docker_variables - tags: - - latest-amd64 - - develop-amd64 - - ${CI_COMMIT_SHA:0:8}-amd64 - - docker-develop-arm64: - image: woodpeckerci/plugin-kaniko:2.3.1 - when: - - evaluate: 'platform == "linux/arm64" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' - settings: - <<: *docker_variables - tags: - - latest-arm64 - - develop-arm64 - - ${CI_COMMIT_SHA:0:8}-arm64 - - docker-stable-amd64: - image: *kaniko_image - when: - - evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG == ""' - settings: - <<: *docker_variables - tags: &amd64_tags - - latest-amd64 - - stable-amd64 - - ${CI_COMMIT_SHA:0:8}-amd64 - - docker-stable-tag-amd64: - image: *kaniko_image - when: - - evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"' - - evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG != ""' - settings: - <<: *docker_variables - tags: - - <<: *amd64_tags - - ${CI_COMMIT_TAG}-amd64 - - docker-stable-arm64: - image: *kaniko_image - when: - - evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG == ""' - settings: - <<: *docker_variables - tags: &arm64_tags - - latest-arm64 - - stable-arm64 - - ${CI_COMMIT_SHA:0:8}-arm64 - - docker-stable-tag-arm64: - image: *kaniko_image - when: - - evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"' - - evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG != ""' - settings: - <<: *docker_variables - tags: - - <<: *arm64_tags - - ${CI_COMMIT_TAG}-arm64 diff --git a/.woodpecker/lint.yaml b/.woodpecker/lint.yaml index 0ab7441a8..b96d584ee 100644 --- a/.woodpecker/lint.yaml +++ b/.woodpecker/lint.yaml @@ -1,24 +1,14 @@ when: - event: pull_request - path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ] + path: [ "*.ex", "*.eex", "*.exs", "mix.lock", ".woodpecker/**" ] - event: push branch: ${CI_REPO_DEFAULT_BRANCH} - path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ] - -labels: - platform: linux/amd64 - -variables: - script_file_entrypoint: &script_file_entrypoint - - /bin/sh - - -c - - 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh' + path: [ "*.ex", "*.eex", "*.exs", "mix.lock", ".woodpecker/**" ] steps: mix-format: image: &elixir-image docker.io/elixir:1.15-alpine - entrypoint: *script_file_entrypoint failure: ignore commands: - | @@ -29,7 +19,6 @@ steps: credo: image: *elixir-image - entrypoint: *script_file_entrypoint failure: ignore environment: MIX_ENV: test @@ -66,7 +55,6 @@ steps: ensure-status: image: *elixir-image - entrypoint: *script_file_entrypoint commands: | if test -f fail.stamp; then echo "One or more previous steps fails. Failing workflow..." diff --git a/.woodpecker/otp-musl.yaml b/.woodpecker/otp-musl.yaml deleted file mode 100644 index 60558b893..000000000 --- a/.woodpecker/otp-musl.yaml +++ /dev/null @@ -1,292 +0,0 @@ -when: - - event: push - branch: ${CI_REPO_DEFAULT_BRANCH} - path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ] - - event: tag - - event: manual - branch: ${CI_REPO_DEFAULT_BRANCH} - - event: manual - branch: stable - -matrix: - platform: - - linux/amd64 - - linux/arm - - linux/arm64 - -# This is needed for the when clauses below. -# When the platform clause is fixed, this might not be needed anymore -labels: - platform: ${platform} - -variables: - script_file_entrypoint: &script_file_entrypoint - - /bin/sh - - -c - - 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh' - build_cmds: &build_cmds - - apk add git build-base cmake file-dev openssl vips-dev zip - - echo "import Config" > config/prod.secret.exs - - mix local.hex --force - - mix local.rebar --force - - mix deps.get --only prod - - mkdir release - - export PLEROMA_BUILD_BRANCH=${CI_COMMIT_BRANCH} - - mix release --path release - build_image_amd64: &build_image_amd64 docker.io/hexpm/elixir-amd64:1.17.3-erlang-27.3.4.2-alpine-3.22.1 - build_image_arm: &build_image_arm docker.io/arm32v7/elixir:1.17.3-alpine - build_image_arm64: &build_image_arm64 docker.io/hexpm/elixir-arm64:1.17.3-erlang-27.3.4.2-alpine-3.22.1 - artifacts_uploader_image: &artifacts_uploader_image docker.io/woodpeckercommunity/plugin-gitea-package:0.5.0 - artifacts_uploader_settings: &artifacts_uploader_settings - user: - from_secret: pleroma-ci-user - password: - from_secret: pleroma-ci-password - owner: 'pleroma' - env: &env - MIX_ENV: prod - VIX_COMPILATION_MODE: PLATFORM_PROVIDED_LIBVIPS - -steps: - otp-develop-amd64-musl: - image: *build_image_amd64 - entrypoint: *script_file_entrypoint - when: - - evaluate: 'platform == "linux/amd64" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' - environment: *env - commands: &amd64_build - - <<: *build_cmds - - zip -9rq ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64-musl.zip release - - otp-stable-amd64-musl: - image: *build_image_amd64 - entrypoint: *script_file_entrypoint - when: - - evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"' - environment: *env - commands: *amd64_build - - # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable - otp-stable-tag-amd64-musl: - image: *build_image_amd64 - entrypoint: *script_file_entrypoint - when: - - evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"' - environment: *env - commands: - - <<: *build_cmds - - zip -9rq stable-${CI_COMMIT_SHA:0:8}-amd64-musl.zip release - - otp-develop-arm-musl: - image: *build_image_arm - entrypoint: *script_file_entrypoint - when: - - evaluate: 'platform == "linux/arm" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' - environment: *env - commands: &arm_build - - <<: *build_cmds - - zip -9rq ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm-musl.zip release - - otp-stable-arm-musl: - image: *build_image_arm - entrypoint: *script_file_entrypoint - when: - - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"' - environment: *env - commands: *arm_build - - # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable - otp-stable-tag-arm-musl: - image: *build_image_arm - entrypoint: *script_file_entrypoint - when: - - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"' - environment: *env - commands: - - <<: *build_cmds - - zip -9rq stable-${CI_COMMIT_SHA:0:8}-arm-musl.zip release - - otp-develop-arm64-musl: - image: *build_image_arm64 - entrypoint: *script_file_entrypoint - when: - - evaluate: 'platform == "linux/arm64" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' - environment: *env - commands: &arm64_build - - <<: *build_cmds - - zip -9rq ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64-musl.zip release - - otp-stable-arm64-musl: - image: *build_image_arm64 - entrypoint: *script_file_entrypoint - when: - - evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"' - environment: *env - commands: *arm64_build - - # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable - otp-stable-tag-arm64-musl: - image: *build_image_arm64 - entrypoint: *script_file_entrypoint - when: - - evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"' - environment: *env - commands: - - <<: *build_cmds - - zip -9rq stable-${CI_COMMIT_SHA:0:8}-arm64-musl.zip release - - upload-artifacts-amd64-musl: - image: *artifacts_uploader_image - when: - - evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' - - evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual"' - settings: - <<: *artifacts_uploader_settings - package_name: pleroma-otp-${CI_COMMIT_BRANCH}-amd64-musl - package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64-musl - file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64-musl.zip - file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64-musl.zip - update: 'true' - - upload-latest-amd64-musl: - image: *artifacts_uploader_image - when: - - evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' - - evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual"' - settings: - <<: *artifacts_uploader_settings - package_name: pleroma-otp-${CI_COMMIT_BRANCH}-amd64-musl - package_version: latest - file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64-musl.zip - file_name: pleroma.zip - update: 'true' - - # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable - upload-artifacts-tag-amd64-musl: - image: *artifacts_uploader_image - when: - - evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"' - settings: - <<: *artifacts_uploader_settings - package_name: pleroma-otp-stable-amd64-musl - package_version: stable-${CI_COMMIT_SHA:0:8}-amd64-musl - file_source: ./stable-${CI_COMMIT_SHA:0:8}-amd64-musl.zip - file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-amd64-musl.zip - update: 'true' - - # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable - upload-latest-tag-amd64-musl: - image: *artifacts_uploader_image - when: - - evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"' - settings: - <<: *artifacts_uploader_settings - package_name: pleroma-otp-stable-amd64-musl - package_version: latest - file_source: ./stable-${CI_COMMIT_SHA:0:8}-amd64-musl.zip - file_name: pleroma.zip - update: 'true' - - upload-artifacts-arm-musl: - image: *artifacts_uploader_image - when: - - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' - - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual"' - settings: - <<: *artifacts_uploader_settings - package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm-musl - package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm-musl - file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm-musl.zip - file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm-musl.zip - update: 'true' - - upload-latest-arm-musl: - image: *artifacts_uploader_image - when: - - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' - - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual"' - settings: - <<: *artifacts_uploader_settings - package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm-musl - package_version: latest - file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm-musl.zip - file_name: pleroma.zip - update: 'true' - - # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable - upload-artifacts-tag-arm-musl: - image: *artifacts_uploader_image - when: - - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"' - settings: - <<: *artifacts_uploader_settings - package_name: pleroma-otp-stable-arm-musl - package_version: stable-${CI_COMMIT_SHA:0:8}-arm-musl - file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm-musl.zip - file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-arm-musl.zip - update: 'true' - - # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable - upload-latest-tag-arm-musl: - image: *artifacts_uploader_image - when: - - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"' - settings: - <<: *artifacts_uploader_settings - package_name: pleroma-otp-stable-arm-musl - package_version: latest - file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm-musl.zip - file_name: pleroma.zip - update: 'true' - - upload-artifacts-arm64-musl: - image: *artifacts_uploader_image - when: - - evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' - - evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual"' - settings: - <<: *artifacts_uploader_settings - package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm64-musl - package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64-musl - file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64-musl.zip - file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64-musl.zip - update: 'true' - - upload-latest-arm64-musl: - image: *artifacts_uploader_image - when: - - evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' - - evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual"' - settings: - <<: *artifacts_uploader_settings - package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm64-musl - package_version: latest - file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64-musl.zip - file_name: pleroma.zip - update: 'true' - - # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable - upload-artifacts-tag-arm64-musl: - image: *artifacts_uploader_image - when: - - evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"' - settings: - <<: *artifacts_uploader_settings - package_name: pleroma-otp-stable-arm64-musl - package_version: stable-${CI_COMMIT_SHA:0:8}-arm64-musl - file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm64-musl.zip - file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-arm64-musl.zip - update: 'true' - - # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable - upload-latest-tag-arm64-musl: - image: *artifacts_uploader_image - when: - - evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"' - settings: - <<: *artifacts_uploader_settings - package_name: pleroma-otp-stable-arm64-musl - package_version: latest - file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm64-musl.zip - file_name: pleroma.zip - update: 'true' diff --git a/.woodpecker/otp.yaml b/.woodpecker/otp.yaml deleted file mode 100644 index 9a33c228e..000000000 --- a/.woodpecker/otp.yaml +++ /dev/null @@ -1,293 +0,0 @@ -when: - - event: push - branch: ${CI_REPO_DEFAULT_BRANCH} - path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ] - - event: tag - - event: manual - branch: ${CI_REPO_DEFAULT_BRANCH} - - event: manual - branch: stable - -matrix: - platform: - - linux/amd64 - - linux/arm - - linux/arm64 - -# This is needed for the when clauses below. -# When the platform clause is fixed, this might not be needed anymore -labels: - platform: ${platform} - -variables: - script_file_entrypoint: &script_file_entrypoint - - /bin/sh - - -c - - 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh' - build_cmds: &build_cmds - - apt-get update && apt-get install -y cmake libmagic-dev libvips-dev erlang-dev git build-essential zip - - echo "import Config" > config/prod.secret.exs - - mix local.hex --force - - mix local.rebar --force - - mix deps.get --only prod - - mkdir release - - export PLEROMA_BUILD_BRANCH=${CI_COMMIT_BRANCH} - - mix release --path release - build_image_amd64: &build_image_amd64 docker.io/hexpm/elixir-amd64:1.17.3-erlang-27.3.4.2-ubuntu-noble-20250716 - build_image_arm: &build_image_arm docker.io/arm32v7/elixir:1.17.3 - build_image_arm64: &build_image_arm64 docker.io/hexpm/elixir-arm64:1.17.3-erlang-27.3.4.2-ubuntu-noble-20250716 - artifacts_uploader_image: &artifacts_uploader_image docker.io/woodpeckercommunity/plugin-gitea-package:0.5.0 - artifacts_uploader_settings: &artifacts_uploader_settings - user: - from_secret: pleroma-ci-user - password: - from_secret: pleroma-ci-password - owner: 'pleroma' - env: &env - MIX_ENV: prod - VIX_COMPILATION_MODE: PLATFORM_PROVIDED_LIBVIPS - DEBIAN_FRONTEND: noninteractive - -steps: - otp-develop-amd64: - image: *build_image_amd64 - entrypoint: *script_file_entrypoint - when: - - evaluate: 'platform == "linux/amd64" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' - environment: *env - commands: &amd64_build - - <<: *build_cmds - - zip -9rq ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64.zip release - - otp-stable-amd64: - image: *build_image_amd64 - entrypoint: *script_file_entrypoint - when: - - evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"' - environment: *env - commands: *amd64_build - - # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable - otp-stable-tag-amd64: - image: *build_image_amd64 - entrypoint: *script_file_entrypoint - when: - - evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"' - environment: *env - commands: - - <<: *build_cmds - - zip -9rq stable-${CI_COMMIT_SHA:0:8}-amd64.zip release - - otp-develop-arm: - image: *build_image_arm - entrypoint: *script_file_entrypoint - when: - - evaluate: 'platform == "linux/arm" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' - environment: *env - commands: &arm_build - - <<: *build_cmds - - zip -9rq ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm.zip release - - otp-stable-arm: - image: *build_image_arm - entrypoint: *script_file_entrypoint - when: - - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"' - environment: *env - commands: *arm_build - - # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable - otp-stable-tag-arm: - image: *build_image_arm - entrypoint: *script_file_entrypoint - when: - - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"' - environment: *env - commands: - - <<: *build_cmds - - zip -9rq stable-${CI_COMMIT_SHA:0:8}-arm.zip release - - otp-develop-arm64: - image: *build_image_arm64 - entrypoint: *script_file_entrypoint - when: - - evaluate: 'platform == "linux/arm64" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' - environment: *env - commands: &arm64_build - - <<: *build_cmds - - zip -9rq ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64.zip release - - otp-stable-arm64: - image: *build_image_arm64 - entrypoint: *script_file_entrypoint - when: - - evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"' - environment: *env - commands: *arm64_build - - # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable - otp-stable-tag-arm64: - image: *build_image_arm64 - entrypoint: *script_file_entrypoint - when: - - evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"' - environment: *env - commands: - - <<: *build_cmds - - zip -9rq stable-${CI_COMMIT_SHA:0:8}-arm64.zip release - - upload-artifacts-amd64: - image: *artifacts_uploader_image - when: - - evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' - - evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual"' - settings: - <<: *artifacts_uploader_settings - package_name: pleroma-otp-${CI_COMMIT_BRANCH}-amd64 - package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64 - file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64.zip - file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64.zip - update: 'true' - - upload-latest-amd64: - image: *artifacts_uploader_image - when: - - evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' - - evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual"' - settings: - <<: *artifacts_uploader_settings - package_name: pleroma-otp-${CI_COMMIT_BRANCH}-amd64 - package_version: latest - file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64.zip - file_name: pleroma.zip - update: 'true' - - # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable - upload-artifacts-tag-amd64: - image: *artifacts_uploader_image - when: - - evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"' - settings: - <<: *artifacts_uploader_settings - package_name: pleroma-otp-stable-amd64 - package_version: stable-${CI_COMMIT_SHA:0:8}-amd64 - file_source: ./stable-${CI_COMMIT_SHA:0:8}-amd64.zip - file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-amd64.zip - update: 'true' - - # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable - upload-latest-tag-amd64: - image: *artifacts_uploader_image - when: - - evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"' - settings: - <<: *artifacts_uploader_settings - package_name: pleroma-otp-stable-amd64 - package_version: latest - file_source: ./stable-${CI_COMMIT_SHA:0:8}-amd64.zip - file_name: pleroma.zip - update: 'true' - - upload-artifacts-arm: - image: *artifacts_uploader_image - when: - - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' - - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual"' - settings: - <<: *artifacts_uploader_settings - package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm - package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm - file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm.zip - file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm.zip - update: 'true' - - upload-latest-arm: - image: *artifacts_uploader_image - when: - - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' - - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual"' - settings: - <<: *artifacts_uploader_settings - package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm - package_version: latest - file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm.zip - file_name: pleroma.zip - update: 'true' - - # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable - upload-artifacts-tag-arm: - image: *artifacts_uploader_image - when: - - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"' - settings: - <<: *artifacts_uploader_settings - package_name: pleroma-otp-stable-arm - package_version: stable-${CI_COMMIT_SHA:0:8}-arm - file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm.zip - file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-arm.zip - update: 'true' - - # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable - upload-latest-tag-arm: - image: *artifacts_uploader_image - when: - - evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"' - settings: - <<: *artifacts_uploader_settings - package_name: pleroma-otp-stable-arm - package_version: latest - file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm.zip - file_name: pleroma.zip - update: 'true' - - upload-artifacts-arm64: - image: *artifacts_uploader_image - when: - - evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' - - evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual"' - settings: - <<: *artifacts_uploader_settings - package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm64 - package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64 - file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64.zip - file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64.zip - update: 'true' - - upload-latest-arm64: - image: *artifacts_uploader_image - when: - - evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"' - - evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual"' - settings: - <<: *artifacts_uploader_settings - package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm64 - package_version: latest - file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64.zip - file_name: pleroma.zip - update: 'true' - - # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable - upload-artifacts-tag-arm64: - image: *artifacts_uploader_image - when: - - evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"' - settings: - <<: *artifacts_uploader_settings - package_name: pleroma-otp-stable-arm64 - package_version: stable-${CI_COMMIT_SHA:0:8}-arm64 - file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm64.zip - file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-arm64.zip - update: 'true' - - # Tag events don't have CI_COMMIT_BRANCH set, hardcode stable - upload-latest-tag-arm64: - image: *artifacts_uploader_image - when: - - evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"' - settings: - <<: *artifacts_uploader_settings - package_name: pleroma-otp-stable-arm64 - package_version: latest - file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm64.zip - file_name: pleroma.zip - update: 'true' diff --git a/.woodpecker/unit-testing-elixir-1.15.yaml b/.woodpecker/unit-testing-elixir-1.15.yaml index a4a8fc266..84046be41 100644 --- a/.woodpecker/unit-testing-elixir-1.15.yaml +++ b/.woodpecker/unit-testing-elixir-1.15.yaml @@ -1,26 +1,16 @@ when: - event: pull_request - path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ] + path: [ "*.ex", "*.eex", "*.exs", "mix.lock", ".woodpecker/**" ] - event: push branch: ${CI_REPO_DEFAULT_BRANCH} - path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ] + path: [ "*.ex", "*.eex", "*.exs", "mix.lock", ".woodpecker/**" ] depends_on: - lint -labels: - platform: linux/amd64 - -variables: - script_file_entrypoint: &script_file_entrypoint - - /bin/sh - - -c - - 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh' - steps: unit-testing-elixir-1.15: image: elixir:1.15-alpine - entrypoint: *script_file_entrypoint environment: MIX_ENV: test DB_HOST: postgres diff --git a/.woodpecker/unit-testing-elixir-1.18.yaml b/.woodpecker/unit-testing-elixir-1.18.yaml index 9ad9eebc9..8dcec62fb 100644 --- a/.woodpecker/unit-testing-elixir-1.18.yaml +++ b/.woodpecker/unit-testing-elixir-1.18.yaml @@ -1,26 +1,16 @@ when: - event: pull_request - path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ] + path: [ "*.ex", "*.eex", "*.exs", "mix.lock", ".woodpecker/**" ] - event: push branch: ${CI_REPO_DEFAULT_BRANCH} - path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ] + path: [ "*.ex", "*.eex", "*.exs", "mix.lock", ".woodpecker/**" ] depends_on: - lint -labels: - platform: linux/amd64 - -variables: - script_file_entrypoint: &script_file_entrypoint - - /bin/sh - - -c - - 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh' - steps: unit-testing-elixir-1.18: image: elixir:1.18-otp-27-alpine - entrypoint: *script_file_entrypoint environment: MIX_ENV: test DB_HOST: postgres diff --git a/CHANGELOG.md b/CHANGELOG.md index f03be3f29..adc76c767 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,60 +4,6 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). -## 2.10.2 - -### Security - -- ActivityPub: Fixed failed-signature inbox retry handling and signer identity checks to prevent spoofed remote activities from being processed - -## 2.10.1 - -### Changed - -- Move avatar_description and header_description fields to the account object -- Update Bandit to 1.10.4 -- No-op code correctness improvements detected by Elixir 1.19 compiler -- Downgrade Hackney to 1.20.1 -- Use a custom redirect handler to ensure MediaProxy redirects are followed with Hackney -- Update Hackney, the default HTTP client, to the latest release which supports Happy Eyeballs for improved IPv6 federation -- Paginate follow requests -- Moved Phoenix LiveDashboard to /pleroma/live_dashboard -- Add mute/block expiry to the relationship object -- Filter indexable activities before inserting indexing jobs into the queue. - -### Added - -- Allow assigning users to reports -- Allow fine-grained announce visibilities -- Add immutable tag on cache-control header for several endpoints that's serving the same exact things. -- Add reasonable defaults for :database_config_whitelist -- Support lists `exclusive` param -- Add v1/instance/domain_blocks endpoint -- Add /api/v2/instance profile fields limits info used by Mastodon -- Added Oban Web dashboard located at /pleroma/oban -- Add instructions on how to run a release in docker, to make it easier to run on older distros. - -### Fixed - -- Fix the daily email digest job which was not executing -- Encode custom emoji URLs in EmojiReact activity tags. -- Gopher: Fix Ranch listener not being stopped properly on Pleroma restart when database configuration is enabled -- Fix fetching Hubzilla Actors with alsoKnownAs as string -- Fix /phoenix/live_dashboard redirect not working when user added a path segment -- Fix 404 error codes for missing static files -- Fix OAuth app registration to accept `redirect_uris` as an array of strings (RFC 7591), while keeping backwards compatibility with string input. -- Correct old migrations for expiring activities and user access tokens. -- Federate `votersCount` correctly -- DB prune: Check if user follows hashtag with no objects before deletion -- Stop the rate limiter from crashing when run with wrong settings. -- Restore embeds route -- ReverseProxy: Recursively follow redirects until redirect_limit is reached -- Fix compilation with vips-8.18.0 with bumping to vix 0.36.0 - -### Removed - -- Docs: Removed outdated, incorrect, unmaintained and inappropriate installation documentation (Arch, NetBSD, NixOS) - ## 2.10 ### Security diff --git a/changelog.d/assign-users.add b/changelog.d/assign-users.add new file mode 100644 index 000000000..f50ad94c6 --- /dev/null +++ b/changelog.d/assign-users.add @@ -0,0 +1 @@ +Allow assigning users to reports \ No newline at end of file diff --git a/changelog.d/avatar-description-mastodon-api.change b/changelog.d/avatar-description-mastodon-api.change new file mode 100644 index 000000000..6a454c01e --- /dev/null +++ b/changelog.d/avatar-description-mastodon-api.change @@ -0,0 +1 @@ +Move avatar_description and header_description fields to the account object diff --git a/changelog.d/bandit.change b/changelog.d/bandit.change new file mode 100644 index 000000000..3831a02c2 --- /dev/null +++ b/changelog.d/bandit.change @@ -0,0 +1 @@ +Update Bandit to 1.10.4 diff --git a/changelog.d/bookmark-folders.ignore b/changelog.d/bookmark-folders.ignore new file mode 100644 index 000000000..8705ac00b --- /dev/null +++ b/changelog.d/bookmark-folders.ignore @@ -0,0 +1 @@ +Various bookmark folders-related improvements diff --git a/changelog.d/boost-visibilities.add b/changelog.d/boost-visibilities.add new file mode 100644 index 000000000..317d9840d --- /dev/null +++ b/changelog.d/boost-visibilities.add @@ -0,0 +1 @@ +Allow fine-grained announce visibilities diff --git a/changelog.d/cache-control-immutable.add b/changelog.d/cache-control-immutable.add new file mode 100644 index 000000000..516db67bf --- /dev/null +++ b/changelog.d/cache-control-immutable.add @@ -0,0 +1 @@ +Add immutable tag on cache-control header for several endpoints that's serving the same exact things. \ No newline at end of file diff --git a/changelog.d/ci-artifacts.skip b/changelog.d/ci-artifacts.skip new file mode 100644 index 000000000..e69de29bb diff --git a/changelog.d/context-cleanup.skip b/changelog.d/context-cleanup.skip deleted file mode 100644 index ae609602d..000000000 --- a/changelog.d/context-cleanup.skip +++ /dev/null @@ -1 +0,0 @@ -litepub-0.1.jsonld cleanup diff --git a/changelog.d/credo-aliases-sort-fixes.skip b/changelog.d/credo-aliases-sort-fixes.skip new file mode 100644 index 000000000..e69de29bb diff --git a/changelog.d/database-config-whitelist.add b/changelog.d/database-config-whitelist.add new file mode 100644 index 000000000..a78960c98 --- /dev/null +++ b/changelog.d/database-config-whitelist.add @@ -0,0 +1 @@ +Add reasonable defaults for :database_config_whitelist \ No newline at end of file diff --git a/changelog.d/elixir-1.19-cherrypicks.change b/changelog.d/elixir-1.19-cherrypicks.change new file mode 100644 index 000000000..7e56be008 --- /dev/null +++ b/changelog.d/elixir-1.19-cherrypicks.change @@ -0,0 +1 @@ +No-op code correctness improvements detected by Elixir 1.19 compiler diff --git a/changelog.d/email_digest.fix b/changelog.d/email_digest.fix new file mode 100644 index 000000000..cd15874a2 --- /dev/null +++ b/changelog.d/email_digest.fix @@ -0,0 +1 @@ +Fix the daily email digest job which was not executing diff --git a/changelog.d/emoji-reaction-url-escape.fix b/changelog.d/emoji-reaction-url-escape.fix new file mode 100644 index 000000000..c3a1c8823 --- /dev/null +++ b/changelog.d/emoji-reaction-url-escape.fix @@ -0,0 +1 @@ +Encode custom emoji URLs in EmojiReact activity tags. diff --git a/changelog.d/exclusive-lists.add b/changelog.d/exclusive-lists.add new file mode 100644 index 000000000..bbd722f07 --- /dev/null +++ b/changelog.d/exclusive-lists.add @@ -0,0 +1 @@ +Support lists `exclusive` param diff --git a/changelog.d/gopher-genserver-crash-on-boot.fix b/changelog.d/gopher-genserver-crash-on-boot.fix new file mode 100644 index 000000000..3b51662be --- /dev/null +++ b/changelog.d/gopher-genserver-crash-on-boot.fix @@ -0,0 +1 @@ +Gopher: Fix Ranch listener not being stopped properly on Pleroma restart when database configuration is enabled diff --git a/changelog.d/hackney-downgrade.change b/changelog.d/hackney-downgrade.change new file mode 100644 index 000000000..a98710692 --- /dev/null +++ b/changelog.d/hackney-downgrade.change @@ -0,0 +1 @@ +Downgrade Hackney to 1.20.1 diff --git a/changelog.d/hackney-mediaproxy.change b/changelog.d/hackney-mediaproxy.change new file mode 100644 index 000000000..10dfb0775 --- /dev/null +++ b/changelog.d/hackney-mediaproxy.change @@ -0,0 +1 @@ +Use a custom redirect handler to ensure MediaProxy redirects are followed with Hackney diff --git a/changelog.d/hackney.change b/changelog.d/hackney.change new file mode 100644 index 000000000..3158cfc77 --- /dev/null +++ b/changelog.d/hackney.change @@ -0,0 +1 @@ +Update Hackney, the default HTTP client, to the latest release which supports Happy Eyeballs for improved IPv6 federation diff --git a/changelog.d/hubzilla-alsoknownas.fix b/changelog.d/hubzilla-alsoknownas.fix new file mode 100644 index 000000000..2a2969807 --- /dev/null +++ b/changelog.d/hubzilla-alsoknownas.fix @@ -0,0 +1 @@ +Fix fetching Hubzilla Actors with alsoKnownAs as string diff --git a/changelog.d/inappropriate-docs.remove b/changelog.d/inappropriate-docs.remove new file mode 100644 index 000000000..699c9186a --- /dev/null +++ b/changelog.d/inappropriate-docs.remove @@ -0,0 +1 @@ +Docs: Removed outdated, incorrect, unmaintained and inappropriate installation documentation (Arch, NetBSD, NixOS) diff --git a/changelog.d/instance-domain-blocks.add b/changelog.d/instance-domain-blocks.add new file mode 100644 index 000000000..85f01c5c2 --- /dev/null +++ b/changelog.d/instance-domain-blocks.add @@ -0,0 +1 @@ +Add v1/instance/domain_blocks endpoint diff --git a/changelog.d/instance-profile-fields.add b/changelog.d/instance-profile-fields.add new file mode 100644 index 000000000..712bd68d9 --- /dev/null +++ b/changelog.d/instance-profile-fields.add @@ -0,0 +1 @@ +Add /api/v2/instance profile fields limits info used by Mastodon diff --git a/changelog.d/lint-warnings.skip b/changelog.d/lint-warnings.skip new file mode 100644 index 000000000..e69de29bb diff --git a/changelog.d/live-dashboard-redirect.fix b/changelog.d/live-dashboard-redirect.fix new file mode 100644 index 000000000..10588d89e --- /dev/null +++ b/changelog.d/live-dashboard-redirect.fix @@ -0,0 +1 @@ +Fix /phoenix/live_dashboard redirect not working when user added a path segment diff --git a/changelog.d/map-side-effects.skip b/changelog.d/map-side-effects.skip new file mode 100644 index 000000000..e69de29bb diff --git a/changelog.d/mfm-backend.add b/changelog.d/mfm-backend.add deleted file mode 100644 index f815c6828..000000000 --- a/changelog.d/mfm-backend.add +++ /dev/null @@ -1 +0,0 @@ -Add backend support for Misskey Markdown (MFM) posts diff --git a/changelog.d/missing-static-file.fix b/changelog.d/missing-static-file.fix new file mode 100644 index 000000000..c7ef805aa --- /dev/null +++ b/changelog.d/missing-static-file.fix @@ -0,0 +1 @@ +Fix 404 error codes for missing static files diff --git a/changelog.d/mix-exs-fix.skip b/changelog.d/mix-exs-fix.skip new file mode 100644 index 000000000..e69de29bb diff --git a/changelog.d/mix-exs-update.skip b/changelog.d/mix-exs-update.skip new file mode 100644 index 000000000..e69de29bb diff --git a/changelog.d/oauth-registration-redirect_uris.fix b/changelog.d/oauth-registration-redirect_uris.fix new file mode 100644 index 000000000..76ace55df --- /dev/null +++ b/changelog.d/oauth-registration-redirect_uris.fix @@ -0,0 +1 @@ +Fix OAuth app registration to accept `redirect_uris` as an array of strings (RFC 7591), while keeping backwards compatibility with string input. diff --git a/changelog.d/oban-web.add b/changelog.d/oban-web.add new file mode 100644 index 000000000..c59e2ebca --- /dev/null +++ b/changelog.d/oban-web.add @@ -0,0 +1 @@ +Added Oban Web dashboard located at /pleroma/oban diff --git a/changelog.d/old-migrations.fix b/changelog.d/old-migrations.fix new file mode 100644 index 000000000..49566c896 --- /dev/null +++ b/changelog.d/old-migrations.fix @@ -0,0 +1 @@ +Correct old migrations for expiring activities and user access tokens. diff --git a/changelog.d/paginate-follow-requests.change b/changelog.d/paginate-follow-requests.change new file mode 100644 index 000000000..1a88995b7 --- /dev/null +++ b/changelog.d/paginate-follow-requests.change @@ -0,0 +1 @@ +Paginate follow requests diff --git a/changelog.d/phoenix-livedashboard-move.change b/changelog.d/phoenix-livedashboard-move.change new file mode 100644 index 000000000..116b1523a --- /dev/null +++ b/changelog.d/phoenix-livedashboard-move.change @@ -0,0 +1 @@ +Moved Phoenix LiveDashboard to /pleroma/live_dashboard diff --git a/changelog.d/pleroma-fe-link.fix b/changelog.d/pleroma-fe-link.fix deleted file mode 100644 index de93f86dd..000000000 --- a/changelog.d/pleroma-fe-link.fix +++ /dev/null @@ -1 +0,0 @@ -Updated Pleroma-FE build URL after Forgejo migration diff --git a/changelog.d/plug-test-typo.skip b/changelog.d/plug-test-typo.skip new file mode 100644 index 000000000..e69de29bb diff --git a/changelog.d/poll-voters-count-inflation.fix b/changelog.d/poll-voters-count-inflation.fix deleted file mode 100644 index 7eae41f13..000000000 --- a/changelog.d/poll-voters-count-inflation.fix +++ /dev/null @@ -1 +0,0 @@ -Fix votersCount inflation when same voter picks multiple options diff --git a/changelog.d/poll-voters-count.fix b/changelog.d/poll-voters-count.fix new file mode 100644 index 000000000..2dbc81b5d --- /dev/null +++ b/changelog.d/poll-voters-count.fix @@ -0,0 +1 @@ +Federate `votersCount` correctly diff --git a/changelog.d/prune-hashtag-follow-3376.fix b/changelog.d/prune-hashtag-follow-3376.fix new file mode 100644 index 000000000..cdb4e9a79 --- /dev/null +++ b/changelog.d/prune-hashtag-follow-3376.fix @@ -0,0 +1 @@ +DB prune: Check if user follows hashtag with no objects before deletion diff --git a/changelog.d/rate-limiter-hardening.fix b/changelog.d/rate-limiter-hardening.fix new file mode 100644 index 000000000..a3af8fcc4 --- /dev/null +++ b/changelog.d/rate-limiter-hardening.fix @@ -0,0 +1 @@ +Stop the rate limiter from crashing when run with wrong settings. diff --git a/changelog.d/reduce-flaky-tests.skip b/changelog.d/reduce-flaky-tests.skip new file mode 100644 index 000000000..0375762c0 --- /dev/null +++ b/changelog.d/reduce-flaky-tests.skip @@ -0,0 +1 @@ +Reduce the number of flaky tests by making them sync if they affect the global state, and silence noisy test output. diff --git a/changelog.d/relationship-expires-at.change b/changelog.d/relationship-expires-at.change new file mode 100644 index 000000000..286dba197 --- /dev/null +++ b/changelog.d/relationship-expires-at.change @@ -0,0 +1 @@ +Add mute/block expiry to the relationship object diff --git a/changelog.d/release-to-docker.add b/changelog.d/release-to-docker.add new file mode 100644 index 000000000..5fbf611a5 --- /dev/null +++ b/changelog.d/release-to-docker.add @@ -0,0 +1 @@ +Add instructions on how to run a release in docker, to make it easier to run on older distros. diff --git a/changelog.d/restore-embeds.fix b/changelog.d/restore-embeds.fix new file mode 100644 index 000000000..5a2a1c4fe --- /dev/null +++ b/changelog.d/restore-embeds.fix @@ -0,0 +1 @@ +Restore embeds route diff --git a/changelog.d/reverseproxy-recursive-redirect.fix b/changelog.d/reverseproxy-recursive-redirect.fix new file mode 100644 index 000000000..744109fd6 --- /dev/null +++ b/changelog.d/reverseproxy-recursive-redirect.fix @@ -0,0 +1 @@ +ReverseProxy: Recursively follow redirects until redirect_limit is reached diff --git a/changelog.d/search-indexing.change b/changelog.d/search-indexing.change new file mode 100644 index 000000000..766934f3f --- /dev/null +++ b/changelog.d/search-indexing.change @@ -0,0 +1 @@ +Filter indexable activities before inserting indexing jobs into the queue. diff --git a/changelog.d/search-indexing.skip b/changelog.d/search-indexing.skip new file mode 100644 index 000000000..e69de29bb diff --git a/changelog.d/twitter-api.skip b/changelog.d/twitter-api.skip new file mode 100644 index 000000000..e69de29bb diff --git a/changelog.d/update-comment.ignore b/changelog.d/update-comment.ignore new file mode 100644 index 000000000..733e813b3 --- /dev/null +++ b/changelog.d/update-comment.ignore @@ -0,0 +1 @@ +Update comment for prepare_object, rename prepare_outgoing diff --git a/changelog.d/user-view.ignore b/changelog.d/user-view.ignore new file mode 100644 index 000000000..37e9a7e09 --- /dev/null +++ b/changelog.d/user-view.ignore @@ -0,0 +1 @@ +Avoid code duplication in UserView diff --git a/changelog.d/vix-0.36.0.fix b/changelog.d/vix-0.36.0.fix new file mode 100644 index 000000000..43a8dd8f8 --- /dev/null +++ b/changelog.d/vix-0.36.0.fix @@ -0,0 +1 @@ +Fix compilation with vips-8.18.0 with bumping to vix 0.36.0 diff --git a/changelog.d/woodpecker-pr-pipeline.skip b/changelog.d/woodpecker-pr-pipeline.skip new file mode 100644 index 000000000..e69de29bb diff --git a/config/config.exs b/config/config.exs index 2d38e3ebe..5bf2c5c2e 100644 --- a/config/config.exs +++ b/config/config.exs @@ -203,8 +203,7 @@ config :pleroma, :instance, "text/plain", "text/html", "text/markdown", - "text/bbcode", - "text/x.misskeymarkdown" + "text/bbcode" ], autofollowed_nicknames: [], autofollowing_nicknames: [], @@ -776,7 +775,7 @@ config :pleroma, :frontends, "name" => "pleroma-fe", "git" => "https://git.pleroma.social/pleroma/pleroma-fe", "build_url" => - "https://git.pleroma.social/api/packages/pleroma/generic/pleroma-fe-builds/${ref}/latest.zip", + "https://git.pleroma.social/pleroma/pleroma-fe/-/jobs/artifacts/${ref}/download?job=build", "ref" => "develop" }, "fedi-fe" => %{ diff --git a/config/description.exs b/config/description.exs index 6e4348907..c388d17c3 100644 --- a/config/description.exs +++ b/config/description.exs @@ -815,8 +815,7 @@ config :pleroma, :config_description, [ "text/plain", "text/html", "text/markdown", - "text/bbcode", - "text/x.misskeymarkdown" + "text/bbcode" ] }, %{ @@ -1395,13 +1394,7 @@ config :pleroma, :config_description, [ label: "Post Content Type", type: {:dropdown, :atom}, description: "Default post formatting option", - suggestions: [ - "text/plain", - "text/html", - "text/markdown", - "text/bbcode", - "text/x.misskeymarkdown" - ] + suggestions: ["text/plain", "text/html", "text/markdown", "text/bbcode"] }, %{ key: :redirectRootNoLogin, diff --git a/lib/pleroma/formatter.ex b/lib/pleroma/formatter.ex index 4bf2f6b95..11d5af2fb 100644 --- a/lib/pleroma/formatter.ex +++ b/lib/pleroma/formatter.ex @@ -127,13 +127,6 @@ defmodule Pleroma.Formatter do Earmark.as_html!(text, %Earmark.Options{compact_output: true, smartypants: false}) end - def markdown_to_html(text, opts) do - Earmark.as_html!( - text, - %Earmark.Options{compact_output: true, smartypants: false} |> Map.merge(opts) - ) - end - def html_escape({text, mentions, hashtags}, type) do {html_escape(text, type), mentions, hashtags} end @@ -142,10 +135,6 @@ defmodule Pleroma.Formatter do HTML.filter_tags(text) end - def html_escape(text, "text/x.misskeymarkdown") do - HTML.filter_tags(text) - end - def html_escape(text, "text/plain") do Regex.split(@link_regex, text, include_captures: true) |> Enum.map_every(2, fn chunk -> diff --git a/lib/pleroma/frontend.ex b/lib/pleroma/frontend.ex index e37a3fefe..e651d7d9d 100644 --- a/lib/pleroma/frontend.ex +++ b/lib/pleroma/frontend.ex @@ -75,8 +75,8 @@ defmodule Pleroma.Frontend do end defp download_build(frontend_info, dest) do + Logger.info("Downloading pre-built bundle for #{frontend_info["name"]}") url = String.replace(frontend_info["build_url"], "${ref}", frontend_info["ref"]) - Logger.info("Downloading pre-built bundle for #{frontend_info["name"]} from #{url}") with {:ok, %{status: 200, body: zip_body}} <- Pleroma.HTTP.get(url, [], pool: :media, recv_timeout: 120_000) do diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index 5e9314446..f1e07a257 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -372,21 +372,13 @@ defmodule Pleroma.Object do option end) - existing_voters = object.data["voters"] || [] - voters = [actor | existing_voters] |> Enum.uniq() - new_voter? = actor not in existing_voters - existing_voters_count = object.data["votersCount"] + voters = [actor | object.data["voters"] || []] |> Enum.uniq() voters_count = - cond do - is_integer(existing_voters_count) and new_voter? -> - existing_voters_count + 1 - - is_integer(existing_voters_count) -> - existing_voters_count - - true -> - length(voters) + if Map.has_key?(object.data, "votersCount") do + object.data["votersCount"] + 1 + else + length(voters) end data = diff --git a/lib/pleroma/release_tasks.ex b/lib/pleroma/release_tasks.ex index 49400940f..af2d35c8f 100644 --- a/lib/pleroma/release_tasks.ex +++ b/lib/pleroma/release_tasks.ex @@ -5,10 +5,7 @@ defmodule Pleroma.ReleaseTasks do @repo Pleroma.Repo - # TODO: Kept for some backwards compatibility with buggy pleroma_ctl, - # if a mismatch between pleroma_ctl and Pleroma accidentaly happens. - # Remove in the future. - def run(args) when is_binary(args) do + def run(args) do [task | args] = String.split(args) case task do @@ -19,20 +16,6 @@ defmodule Pleroma.ReleaseTasks do end end - # HACK: Script arguments need to be received as a list, otherwise (quoted) arguments with - # whitespace will be broken. Previously the broken string form above was used, - # escaping in the shell does not help. - def run(args) when is_list(args) do - [task | args] = args - - case task do - "migrate" -> migrate(args) - "create" -> create() - "rollback" -> rollback(args) - task -> mix_task(task, args) - end - end - def find_module(task) do module_name = task diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 0b513ee16..071d634db 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1677,80 +1677,44 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do show_birthday = !!birthday - with {:ok, nickname} <- nickname_from_actor(data, additional) do - {:ok, - %{ - ap_id: data["id"], - uri: get_actor_url(data["url"]), - banner: normalize_image(data["image"]), - fields: fields, - emoji: emojis, - is_locked: is_locked, - is_discoverable: is_discoverable, - invisible: invisible, - avatar: normalize_image(data["icon"]), - name: data["name"], - follower_address: data["followers"], - following_address: data["following"], - featured_address: featured_address, - bio: data["summary"] || "", - actor_type: actor_type, - also_known_as: normalize_also_known_as(data["alsoKnownAs"]), - public_key: public_key, - inbox: data["inbox"], - shared_inbox: shared_inbox, - accepts_chat_messages: accepts_chat_messages, - birthday: birthday, - show_birthday: show_birthday, - pinned_objects: pinned_objects, - nickname: nickname - }} - end - end + # if WebFinger request was already done, we probably have acct, otherwise + # we request WebFinger here + nickname = additional[:nickname_from_acct] || generate_nickname(data) - defp nickname_from_actor(data, additional) do - generated = generated_nickname(data) - - case additional[:nickname_from_acct] do - ^generated when is_binary(generated) -> - {:ok, generated} - - acct when is_binary(acct) -> - with ^acct <- webfinger_nickname(data) do - {:ok, acct} - else - _ -> {:error, {:webfinger_actor_mismatch, acct, data["id"]}} - end - - _ -> - {:ok, generate_nickname(data)} - end - end - - defp generated_nickname(%{"preferredUsername" => username, "id" => ap_id}) - when is_binary(username) and is_binary(ap_id) do - case URI.parse(ap_id) do - %URI{host: host} when is_binary(host) -> "#{username}@#{host}" - _ -> nil - end - end - - defp generated_nickname(_), do: nil - - defp webfinger_nickname(data) do - with generated when is_binary(generated) <- generated_nickname(data), - {:ok, %{"subject" => "acct:" <> acct, "ap_id" => ap_id}} <- WebFinger.finger(generated), - true <- ap_id == data["id"] do - acct - end + %{ + ap_id: data["id"], + uri: get_actor_url(data["url"]), + banner: normalize_image(data["image"]), + fields: fields, + emoji: emojis, + is_locked: is_locked, + is_discoverable: is_discoverable, + invisible: invisible, + avatar: normalize_image(data["icon"]), + name: data["name"], + follower_address: data["followers"], + following_address: data["following"], + featured_address: featured_address, + bio: data["summary"] || "", + actor_type: actor_type, + also_known_as: normalize_also_known_as(data["alsoKnownAs"]), + public_key: public_key, + inbox: data["inbox"], + shared_inbox: shared_inbox, + accepts_chat_messages: accepts_chat_messages, + birthday: birthday, + show_birthday: show_birthday, + pinned_objects: pinned_objects, + nickname: nickname + } end defp generate_nickname(%{"preferredUsername" => username} = data) when is_binary(username) do - generated = generated_nickname(data) + generated = "#{username}@#{URI.parse(data["id"]).host}" if Config.get([WebFinger, :update_nickname_on_user_fetch]) do - case webfinger_nickname(data) do - acct when is_binary(acct) -> acct + case WebFinger.finger(generated) do + {:ok, %{"subject" => "acct:" <> acct}} -> acct _ -> generated end else @@ -1830,11 +1794,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp collection_private(_data), do: {:ok, true} def user_data_from_user_object(data, additional \\ []) do - with {:ok, data} <- MRF.filter(data), - {:ok, data} <- object_to_user_data(data, additional) do - {:ok, data} + with {:ok, data} <- MRF.filter(data) do + {:ok, object_to_user_data(data, additional)} else - {:error, _} = e -> e e -> {:error, e} end end diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index 2bfff6968..4f1613a07 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -348,7 +348,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do end def inbox(%{assigns: %{valid_signature: false}} = conn, params) do - Federator.incoming_failed_signature_ap_doc(%{ + Federator.incoming_ap_doc(%{ method: conn.method, req_headers: conn.req_headers, request_path: conn.request_path, diff --git a/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex b/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex index 844ad2c8f..c0626ce4d 100644 --- a/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex @@ -6,12 +6,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do use Ecto.Schema alias Pleroma.EctoType.ActivityPub.ObjectValidators - alias Pleroma.HTML - alias Pleroma.User alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.CommonAPI.Utils import Ecto.Changeset @@ -29,7 +26,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do end field(:replies, {:array, ObjectValidators.ObjectID}, default: []) - field(:source, :map) end def cast_and_apply(data) do @@ -84,113 +80,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do def fix_attachments(data), do: data - defp remote_mention_resolver( - %{"id" => ap_id, "tag" => tags}, - "@" <> nickname = mention, - buffer, - opts, - acc - ) - when is_binary(ap_id) and is_list(tags) do - initial_host = - ap_id - |> URI.parse() - |> Map.get(:host) - - with mention_tag when not is_nil(mention_tag) <- - Enum.find(tags, &mention_tag?(&1, mention, initial_host)), - href when is_binary(href) <- mention_tag["href"], - %User{} = user <- User.get_cached_by_ap_id(href) do - link = Pleroma.Formatter.mention_from_user(user, opts) - {link, %{acc | mentions: MapSet.put(acc.mentions, {"@" <> nickname, user})}} - else - _ -> {buffer, acc} - end - end - - defp remote_mention_resolver(_object, _mention, buffer, _opts, acc), do: {buffer, acc} - - defp mention_tag?(%{"type" => "Mention", "name" => name}, mention, initial_host) - when is_binary(name) do - name == mention || mention == "#{name}@#{initial_host}" - end - - defp mention_tag?(_tag, _mention, _initial_host), do: false - - defp scrub_content(%{"content" => content} = object) when is_binary(content) do - Map.put(object, "content", HTML.filter_tags(content)) - end - - defp scrub_content(object), do: object - - defp mfm_parse_limit do - min(Pleroma.Config.get([:instance, :limit]), Pleroma.Config.get([:instance, :remote_limit])) - end - - defp normalize_source(%{"source" => source} = object) when is_binary(source) do - object - |> Map.put("source", %{"content" => source}) - |> normalize_source() - end - - defp normalize_source(%{"source" => source} = object) when is_map(source) do - source = - case source["content"] do - content when is_binary(content) -> - if String.length(content) <= mfm_parse_limit() do - source - else - Map.delete(source, "content") - end - - nil -> - source - - _ -> - Map.delete(source, "content") - end - - Map.put(object, "source", source) - end - - defp normalize_source(object), do: object - - defp fix_misskey_content(%{"htmlMfm" => true, "content" => content} = object) - when is_binary(content) do - Map.put(object, "content", HTML.filter_tags(content)) - end - - defp fix_misskey_content(%{"htmlMfm" => true} = object), do: object - - defp fix_misskey_content( - %{"source" => %{"mediaType" => "text/x.misskeymarkdown", "content" => content}} = object - ) - when is_binary(content) do - mention_handler = fn nick, buffer, opts, acc -> - remote_mention_resolver(object, nick, buffer, opts, acc) - end - - {linked, _mentions, _tags} = - Utils.format_input(content, "text/x.misskeymarkdown", mention_handler: mention_handler) - - Map.put(object, "content", linked) - end - - defp fix_misskey_content(%{"source" => %{"mediaType" => "text/x.misskeymarkdown"}} = object), - do: scrub_content(object) - - defp fix_misskey_content(%{"_misskey_content" => content} = object) when is_binary(content) do - object - |> Map.put("source", %{ - "content" => content, - "mediaType" => "text/x.misskeymarkdown" - }) - |> Map.delete("_misskey_content") - |> fix_misskey_content() - end - - defp fix_misskey_content(object), do: object - defp fix(data) do data |> CommonFixes.fix_actor() @@ -199,8 +88,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do |> fix_tag() |> fix_replies() |> fix_attachments() - |> normalize_source() - |> fix_misskey_content() |> CommonFixes.fix_quote_url() |> CommonFixes.fix_likes() |> Transmogrifier.fix_emoji() diff --git a/lib/pleroma/web/activity_pub/object_validators/common_fields.ex b/lib/pleroma/web/activity_pub/object_validators/common_fields.ex index 9b8580200..22cf0cc05 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fields.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fields.ex @@ -32,7 +32,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFields do quote bind_quoted: binding() do field(:content, :string) field(:contentMap, ObjectValidators.ContentLanguageMap) - field(:htmlMfm, :boolean) field(:published, ObjectValidators.DateTime) field(:updated, ObjectValidators.DateTime) diff --git a/lib/pleroma/web/activity_pub/object_validators/update_validator.ex b/lib/pleroma/web/activity_pub/object_validators/update_validator.ex index 4c0d9dff7..aab90235f 100644 --- a/lib/pleroma/web/activity_pub/object_validators/update_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/update_validator.ex @@ -75,40 +75,15 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator do end end - # For remote Updates, verify the Actor is the same + # For remote Updates, verify the host is the same. def validate_updating_rights_remote(cng) do with actor = get_field(cng, :actor), object = get_field(cng, :object), {:ok, object_id} <- ObjectValidators.ObjectID.cast(object), - entity <- - Object.normalize(object_id, fetch: false) || User.get_cached_by_ap_id(object_id) do - case entity do - # Actor must own Object to update it - %Object{} -> - if actor == entity.data["actor"] do - cng - else - cng - |> add_error(:object, "Can't be updated by this actor") - end - - # Actor must only be allowed to update itself - %User{} -> - if actor == entity.ap_id do - cng - else - cng - |> add_error(:object, "Can't be updated by this actor") - end - - nil -> - cng - |> add_error(:object, "Can't be updated by this actor") - - _ -> - cng - |> add_error(:object, "Update is neither for Object or Actor") - end + actor_uri <- URI.parse(actor), + object_uri <- URI.parse(object_id), + true <- actor_uri.host == object_uri.host do + cng else _e -> cng diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 0af4ceaf5..43c0f456d 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -120,8 +120,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do "https://www.w3.org/ns/activitystreams", "#{Endpoint.url()}/schemas/litepub-0.1.jsonld", %{ - "@language" => get_language(data), - "htmlMfm" => "https://w3id.org/fep/c16b#htmlMfm" + "@language" => get_language(data) } ] } diff --git a/lib/pleroma/web/common_api/activity_draft.ex b/lib/pleroma/web/common_api/activity_draft.ex index 6072fff6b..16489663a 100644 --- a/lib/pleroma/web/common_api/activity_draft.ex +++ b/lib/pleroma/web/common_api/activity_draft.ex @@ -317,7 +317,6 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do emoji = Map.merge(emoji, summary_emoji) - media_type = Utils.get_content_type(draft.params[:content_type]) {:ok, note_data, _meta} = Builder.note(draft) object = @@ -325,18 +324,14 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do |> Map.put("emoji", emoji) |> Map.put("source", %{ "content" => draft.status, - "mediaType" => media_type + "mediaType" => Utils.get_content_type(draft.params[:content_type]) }) - |> maybe_put("htmlMfm", true, media_type == "text/x.misskeymarkdown") |> Map.put("generator", draft.params[:generator]) |> Map.put("language", draft.language) %{draft | object: object} end - defp maybe_put(map, key, value, true), do: Map.put(map, key, value) - defp maybe_put(map, _key, _value, _condition), do: map - defp preview?(%__MODULE__{} = draft) do preview? = Pleroma.Web.Utils.Params.truthy_param?(draft.params[:preview]) %{draft | preview?: preview?} diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 26034d685..32572a721 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -322,14 +322,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do |> Formatter.linkify(options) end - def format_input(text, "text/x.misskeymarkdown", options) do - text - |> Formatter.markdown_to_html(%{breaks: true}) - |> safe_mfm_to_html() - |> Formatter.linkify(options) - |> Formatter.html_escape("text/x.misskeymarkdown") - end - def format_input(text, "text/markdown", options) do text |> Formatter.mentions_escape(options) @@ -338,16 +330,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do |> Formatter.html_escape("text/html") end - defp safe_mfm_to_html(html) do - html - |> MfmParser.Parser.parse() - |> MfmParser.Encoder.to_html() - rescue - _ -> html - catch - _, _ -> html - end - def format_naive_asctime(date) do date |> DateTime.from_naive!("Etc/UTC") |> format_asctime end diff --git a/lib/pleroma/web/federator.ex b/lib/pleroma/web/federator.ex index 90cd2e54a..676fc5137 100644 --- a/lib/pleroma/web/federator.ex +++ b/lib/pleroma/web/federator.ex @@ -11,7 +11,6 @@ defmodule Pleroma.Web.Federator do alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Workers.PublisherWorker alias Pleroma.Workers.ReceiverWorker - alias Pleroma.Workers.SignatureRetryWorker require Logger @@ -36,21 +35,12 @@ defmodule Pleroma.Web.Federator do end # Client API - def incoming_failed_signature_ap_doc(%{ - method: method, - params: params, - req_headers: req_headers, - request_path: request_path, - query_string: query_string - }) do - SignatureRetryWorker.new( + def incoming_ap_doc(%{params: params, req_headers: req_headers}) do + ReceiverWorker.new( %{ - "op" => "incoming_failed_signature_ap_doc", - "method" => method, + "op" => "incoming_ap_doc", "req_headers" => req_headers, "params" => params, - "request_path" => request_path, - "query_string" => query_string, "timeout" => :timer.seconds(20) }, priority: 2 diff --git a/lib/pleroma/web/mastodon_api/views/poll_view.ex b/lib/pleroma/web/mastodon_api/views/poll_view.ex index f047804e2..3b4271227 100644 --- a/lib/pleroma/web/mastodon_api/views/poll_view.ex +++ b/lib/pleroma/web/mastodon_api/views/poll_view.ex @@ -71,12 +71,12 @@ defmodule Pleroma.Web.MastodonAPI.PollView do end) end - defp voters_count(%{data: %{"votersCount" => voters}}) when is_integer(voters), do: voters - defp voters_count(%{data: %{"voters" => [_ | _] = voters}}) do length(voters) end + defp voters_count(%{data: %{"votersCount" => voters}}), do: voters + defp voters_count(_), do: 0 defp voted_and_own_votes(%{object: object} = params, options) do diff --git a/lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex b/lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex index a688b1780..c6d531086 100644 --- a/lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex +++ b/lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex @@ -32,8 +32,8 @@ defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlug do # remove me once testsuite uses mapped capabilities instead of what we do now {:user, nil} -> Logger.debug("Failed to map identity from signature (lookup failure)") - Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{inspect(actor)}") - assign(conn, :valid_signature, false) + Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{actor}") + conn end end diff --git a/lib/pleroma/workers/receiver_worker.ex b/lib/pleroma/workers/receiver_worker.ex index 3afbe138d..e2c950967 100644 --- a/lib/pleroma/workers/receiver_worker.ex +++ b/lib/pleroma/workers/receiver_worker.ex @@ -4,37 +4,40 @@ defmodule Pleroma.Workers.ReceiverWorker do alias Pleroma.Instances + alias Pleroma.Signature + alias Pleroma.User alias Pleroma.Web.Federator - alias Pleroma.Workers.SignatureRetryWorker use Oban.Worker, queue: :federator_incoming, max_attempts: 5, unique: [period: :infinity] @impl true - def perform(%Job{args: %{"op" => "incoming_ap_doc", "params" => params} = args} = job) do - if signature_retry_job?(args) do - perform_signature_retry(job) - else - perform_incoming(params) - end - end - def perform(%Job{args: %{"op" => "incoming_ap_doc"} = args} = job) do - if signature_retry_job?(args) do - perform_signature_retry(job) - else - process_errors(:missing_incoming_ap_doc_params) - end - end + def perform(%Job{ + args: %{ + "op" => "incoming_ap_doc", + "method" => method, + "params" => params, + "req_headers" => req_headers, + "request_path" => request_path, + "query_string" => query_string + } + }) do + # Oban's serialization converts our tuple headers to lists. + # Revert it for the signature validation. + req_headers = Enum.into(req_headers, [], &List.to_tuple(&1)) - defp perform_signature_retry(%Job{args: args} = job) do - SignatureRetryWorker.perform(%Job{ - job - | args: Map.put(args, "op", "incoming_failed_signature_ap_doc") - }) - end + conn_data = %Plug.Conn{ + method: method, + params: params, + req_headers: req_headers, + request_path: request_path, + query_string: query_string + } - defp perform_incoming(params) do - with {:ok, res} <- Federator.perform(:incoming_ap_doc, params) do + with {:ok, %User{}} <- User.get_or_fetch_by_ap_id(conn_data.params["actor"]), + {:ok, _public_key} <- Signature.refetch_public_key(conn_data), + {:signature, true} <- {:signature, Signature.validate_signature(conn_data)}, + {:ok, res} <- Federator.perform(:incoming_ap_doc, params) do unless Instances.reachable?(params["actor"]) do domain = URI.parse(params["actor"]).host Oban.insert(Pleroma.Workers.ReachabilityWorker.new(%{"domain" => domain})) @@ -46,8 +49,17 @@ defmodule Pleroma.Workers.ReceiverWorker do end end - defp signature_retry_job?(args) do - Enum.any?(~w(method req_headers request_path query_string), &Map.has_key?(args, &1)) + def perform(%Job{args: %{"op" => "incoming_ap_doc", "params" => params}}) do + with {:ok, res} <- Federator.perform(:incoming_ap_doc, params) do + unless Instances.reachable?(params["actor"]) do + domain = URI.parse(params["actor"]).host + Oban.insert(Pleroma.Workers.ReachabilityWorker.new(%{"domain" => domain})) + end + + {:ok, res} + else + e -> process_errors(e) + end end @impl true @@ -73,12 +85,10 @@ defmodule Pleroma.Workers.ReceiverWorker do {:error, {:reject, _} = reason} -> {:cancel, reason} # HTTP Sigs {:signature, false} -> {:cancel, :invalid_signature} - {:same_actor, false} -> {:cancel, :actor_signature_mismatch} # Origin / URL validation failed somewhere possibly due to spoofing {:error, :origin_containment_failed} -> {:cancel, :origin_containment_failed} # Unclear if this can be reached {:error, {:side_effects, {:error, :no_object_actor}} = reason} -> {:cancel, reason} - :missing_incoming_ap_doc_params -> {:cancel, :missing_incoming_ap_doc_params} # Catchall {:error, _} = e -> e e -> {:error, e} diff --git a/lib/pleroma/workers/signature_retry_worker.ex b/lib/pleroma/workers/signature_retry_worker.ex deleted file mode 100644 index 2c4c097dd..000000000 --- a/lib/pleroma/workers/signature_retry_worker.ex +++ /dev/null @@ -1,254 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2022 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Workers.SignatureRetryWorker do - alias Pleroma.Instances - alias Pleroma.Signature - alias Pleroma.User - alias Pleroma.Web.ActivityPub.Utils - alias Pleroma.Web.Federator - alias Pleroma.Web.Plugs.MappedSignatureToIdentityPlug - - require Logger - - use Oban.Worker, queue: :federator_incoming, max_attempts: 5, unique: [period: :infinity] - - @impl true - def perform(%Job{ - args: %{ - "op" => "incoming_failed_signature_ap_doc", - "method" => method, - "params" => params, - "req_headers" => req_headers, - "request_path" => request_path, - "query_string" => query_string - } - }) - when is_binary(method) and is_map(params) and is_list(req_headers) and - is_binary(request_path) and is_binary(query_string) do - case normalize_req_headers(req_headers) do - {:ok, req_headers} -> - conn_data = %Plug.Conn{ - assigns: %{valid_signature: true}, - method: method, - params: params, - req_headers: req_headers, - request_path: request_path, - query_string: query_string - } - - signature_actor_result = signature_actor_id(conn_data) - - with actor_id = Utils.get_ap_id(params["actor"]), - {:signature_actor, {:ok, signature_actor_id}} <- - {:signature_actor, signature_actor_result}, - {:same_actor, true} <- {:same_actor, signature_actor_id == actor_id}, - {:ok, %User{}} <- User.get_or_fetch_by_ap_id(actor_id), - {:ok, _public_key} <- Signature.refetch_public_key(conn_data), - {:signature, true} <- {:signature, validate_signature(conn_data)}, - {:same_actor, true} <- {:same_actor, validate_same_actor(conn_data)}, - {:ok, res} <- Federator.perform(:incoming_ap_doc, params) do - unless Instances.reachable?(params["actor"]) do - domain = URI.parse(params["actor"]).host - Oban.insert(Pleroma.Workers.ReachabilityWorker.new(%{"domain" => domain})) - end - - {:ok, res} - else - e -> process_errors(e, retry_log_context(params, request_path, signature_actor_result)) - end - - e -> - process_errors(e, retry_log_context(params, request_path, nil)) - end - end - - def perform(%Job{args: %{"op" => "incoming_failed_signature_ap_doc"} = args}) do - process_errors( - :missing_signature_retry_metadata, - retry_log_context(Map.get(args, "params"), Map.get(args, "request_path"), nil) - ) - end - - def perform(%Job{args: args}) when is_map(args) do - process_errors( - :missing_signature_retry_metadata, - retry_log_context(Map.get(args, "params"), Map.get(args, "request_path"), nil) - ) - end - - def perform(%Job{}), do: process_errors(:missing_signature_retry_metadata) - - @impl true - def timeout(%_{args: %{"timeout" => timeout}}), do: timeout - - def timeout(_job), do: :timer.seconds(5) - - defp normalize_req_headers(req_headers) do - req_headers - |> Enum.reduce_while({:ok, []}, fn - {key, value}, {:ok, acc} when is_binary(key) and is_binary(value) -> - {:cont, {:ok, [{key, value} | acc]}} - - [key, value], {:ok, acc} when is_binary(key) and is_binary(value) -> - {:cont, {:ok, [{key, value} | acc]}} - - _, _ -> - {:halt, {:error, :invalid_signature_retry_metadata}} - end) - |> case do - {:ok, headers} -> {:ok, Enum.reverse(headers)} - error -> error - end - end - - defp validate_same_actor(conn_data) do - case MappedSignatureToIdentityPlug.call(conn_data, []) do - %Plug.Conn{assigns: %{valid_signature: true}} -> - true - - _ -> - false - end - end - - defp validate_signature(conn_data) do - Signature.validate_signature(conn_data) - rescue - _ -> false - catch - _, _ -> false - end - - defp signature_actor_id(conn_data) do - Signature.get_actor_id(conn_data) - rescue - _ -> {:error, :invalid_signature} - catch - _, _ -> {:error, :invalid_signature} - end - - defp process_errors(errors, context \\ %{}) - - defp process_errors({:error, {:error, _} = error}, context), do: process_errors(error, context) - - defp process_errors(errors, context) do - result = - case errors do - # User fetch failures - {:error, :not_found} = reason -> - {:cancel, reason} - - {:error, :forbidden} = reason -> - {:cancel, reason} - - # Inactive user - {:error, {:user_active, false} = reason} -> - {:cancel, reason} - - # Validator will error and return a changeset error - # e.g., duplicate activities or if the object was deleted - {:error, {:validate, {:error, _changeset} = reason}} -> - {:cancel, reason} - - # Duplicate detection during Normalization - {:error, :already_present} -> - {:cancel, :already_present} - - # MRFs will return a reject - {:error, {:reject, _} = reason} -> - {:cancel, reason} - - # HTTP Sigs - {:signature_actor, {:error, _}} -> - {:cancel, :invalid_signature} - - {:signature, false} -> - {:cancel, :invalid_signature} - - {:same_actor, false} -> - {:cancel, :actor_signature_mismatch} - - # Origin / URL validation failed somewhere possibly due to spoofing - {:error, :origin_containment_failed} -> - {:cancel, :origin_containment_failed} - - # Unclear if this can be reached - {:error, {:side_effects, {:error, :no_object_actor}} = reason} -> - {:cancel, reason} - - # Fail closed if the retry cannot reconstruct the original request. - :missing_signature_retry_metadata -> - {:cancel, :missing_signature_retry_metadata} - - {:error, :invalid_signature_retry_metadata} -> - {:cancel, :invalid_signature_retry_metadata} - - # Catchall - {:error, _} = e -> - e - - e -> - {:error, e} - end - - log_signature_retry_rejection(result, context) - result - end - - defp retry_log_context(params, request_path, signature_actor_result) when is_map(params) do - signature_actor = - case signature_actor_result do - {:ok, actor} when is_binary(actor) -> actor - actor when is_binary(actor) -> actor - _ -> nil - end - - %{ - activity_id: params["id"], - payload_actor: Utils.get_ap_id(params["actor"]), - request_path: request_path, - signature_actor: signature_actor, - type: params["type"] - } - end - - defp retry_log_context(_params, request_path, signature_actor_result) do - signature_actor = - case signature_actor_result do - {:ok, actor} when is_binary(actor) -> actor - actor when is_binary(actor) -> actor - _ -> nil - end - - %{ - activity_id: nil, - payload_actor: nil, - request_path: request_path, - signature_actor: signature_actor, - type: nil - } - end - - defp log_signature_retry_rejection({:cancel, reason}, context) - when reason in [ - :actor_signature_mismatch, - :invalid_signature, - :invalid_signature_retry_metadata, - :missing_signature_retry_metadata, - :origin_containment_failed - ] do - Logger.warning( - "Failed-signature inbox retry rejected " <> - "reason=#{inspect(reason)} " <> - "payload_actor=#{inspect(context[:payload_actor])} " <> - "signature_actor=#{inspect(context[:signature_actor])} " <> - "activity_id=#{inspect(context[:activity_id])} " <> - "type=#{inspect(context[:type])} " <> - "request_path=#{inspect(context[:request_path])}" - ) - end - - defp log_signature_retry_rejection(_result, _context), do: :ok -end diff --git a/mix.exs b/mix.exs index 88f98d54e..5d84c6e09 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do def project do [ app: :pleroma, - version: version("2.10.2"), + version: version("2.10.0"), elixir: "~> 1.15", elixirc_paths: elixirc_paths(Mix.env()), compilers: Mix.compilers(), @@ -160,9 +160,6 @@ defmodule Pleroma.Mixfile do {:sweet_xml, "~> 0.7.5"}, {:earmark, "1.4.46"}, {:bbcode_pleroma, "~> 0.2.0"}, - {:mfm_parser, - git: "https://akkoma.dev/AkkomaGang/mfm-parser.git", - ref: "360a30267a847810a63ab48f606ba227b2ca05f0"}, {:cors_plug, "~> 2.0"}, {:web_push_encryption, "~> 0.3.1"}, {:swoosh, "~> 1.16.12"}, diff --git a/mix.lock b/mix.lock index 976a856b0..dd5f65f11 100644 --- a/mix.lock +++ b/mix.lock @@ -79,7 +79,6 @@ "makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"}, "meck": {:hex, :meck, "0.9.2", "85ccbab053f1db86c7ca240e9fc718170ee5bda03810a6292b5306bf31bae5f5", [:rebar3], [], "hexpm", "81344f561357dc40a8344afa53767c32669153355b626ea9fcbc8da6b3045826"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, - "mfm_parser": {:git, "https://akkoma.dev/AkkomaGang/mfm-parser.git", "360a30267a847810a63ab48f606ba227b2ca05f0", [ref: "360a30267a847810a63ab48f606ba227b2ca05f0"]}, "mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"}, "mimerl": {:hex, :mimerl, "1.5.0", "f35aca6f23242339b3666e0ac0702379e362b469d0aea167f6cc713547e777ed", [:rebar3], [], "hexpm", "db648ce065bae14ea84ca8b5dd123f42f49417cef693541110bf6f9e9be9ecc4"}, "mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"}, diff --git a/priv/scrubbers/default.ex b/priv/scrubbers/default.ex index 342ef9944..0defdc74e 100644 --- a/priv/scrubbers/default.ex +++ b/priv/scrubbers/default.ex @@ -82,50 +82,12 @@ defmodule Pleroma.HTML.Scrubber.Default do "recipients-inline", "quote-inline", "invisible", - "ellipsis", - "mfm-center", - "mfm-flip", - "mfm-font", - "mfm-blur", - "mfm-rotate", - "mfm-x2", - "mfm-x3", - "mfm-x4", - "mfm-position", - "mfm-scale", - "mfm-fg", - "mfm-bg", - "mfm-jelly", - "mfm-twitch", - "mfm-shake", - "mfm-spin", - "mfm-jump", - "mfm-bounce", - "mfm-rainbow", - "mfm-tada", - "mfm-sparkle" + "ellipsis" ]) Meta.allow_tag_with_this_attribute_values(:p, "class", ["quote-inline"]) - Meta.allow_tag_with_these_attributes(:span, [ - "lang", - "data-mfm-h", - "data-mfm-v", - "data-mfm-x", - "data-mfm-y", - "data-mfm-alternate", - "data-mfm-speed", - "data-mfm-deg", - "data-mfm-left", - "data-mfm-serif", - "data-mfm-monospace", - "data-mfm-cursive", - "data-mfm-fantasy", - "data-mfm-emoji", - "data-mfm-math", - "data-mfm-color" - ]) + Meta.allow_tag_with_these_attributes(:span, ["lang"]) Meta.allow_tag_with_this_attribute_values(:code, "class", ["inline"]) diff --git a/priv/static/schemas/litepub-0.1.jsonld b/priv/static/schemas/litepub-0.1.jsonld index 4e82d4b01..3569165a4 100644 --- a/priv/static/schemas/litepub-0.1.jsonld +++ b/priv/static/schemas/litepub-0.1.jsonld @@ -4,55 +4,46 @@ "https://w3id.org/security/v1", "https://purl.archive.org/socialweb/webfinger", { - "as": "https://www.w3.org/ns/activitystreams#", - "ostatus": "http://ostatus.org#", - "schema": "http://schema.org#", - "vcard": "http://www.w3.org/2006/vcard/ns#", - - "fedibird": "http://fedibird.com/ns#", - "litepub": "http://litepub.social/ns#", - "sm": "http://smithereen.software/ns#", - "toot": "http://joinmastodon.org/ns#", - - "alsoKnownAs": { - "@id": "as:alsoKnownAs", - "@type": "@id" - }, + "Emoji": "toot:Emoji", "Hashtag": "as:Hashtag", - "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", - "quoteUrl": "as:quoteUrl", - "sensitive": "as:sensitive", - + "PropertyValue": "schema:PropertyValue", "atomUri": "ostatus:atomUri", "conversation": { "@id": "ostatus:conversation", "@type": "@id" }, - - "PropertyValue": "schema:PropertyValue", - "value": "schema:value", - - "quoteUri": "fedibird:quoteUri", - + "discoverable": "toot:discoverable", + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", "capabilities": "litepub:capabilities", - "ChatMessage": "litepub:ChatMessage", - "directMessage": "litepub:directMessage", - "EmojiReact": "litepub:EmojiReact", - "formerRepresentations": "litepub:formerRepresentations", + "ostatus": "http://ostatus.org#", + "schema": "http://schema.org#", + "toot": "http://joinmastodon.org/ns#", + "fedibird": "http://fedibird.com/ns#", + "value": "schema:value", + "sensitive": "as:sensitive", + "litepub": "http://litepub.social/ns#", "invisible": "litepub:invisible", + "directMessage": "litepub:directMessage", "listMessage": { "@id": "litepub:listMessage", "@type": "@id" }, + "quoteUrl": "as:quoteUrl", + "quoteUri": "fedibird:quoteUri", "oauthRegistrationEndpoint": { "@id": "litepub:oauthRegistrationEndpoint", "@type": "@id" }, - - "nonAnonymous": "sm:nonAnonymous", - - "discoverable": "toot:discoverable", - "Emoji": "toot:Emoji" + "EmojiReact": "litepub:EmojiReact", + "ChatMessage": "litepub:ChatMessage", + "alsoKnownAs": { + "@id": "as:alsoKnownAs", + "@type": "@id" + }, + "vcard": "http://www.w3.org/2006/vcard/ns#", + "formerRepresentations": "litepub:formerRepresentations", + "sm": "http://smithereen.software/ns#", + "nonAnonymous": "sm:nonAnonymous" } ] -} \ No newline at end of file +} diff --git a/rel/files/bin/pleroma_ctl b/rel/files/bin/pleroma_ctl index f5d3f321b..6f0dba3a8 100755 --- a/rel/files/bin/pleroma_ctl +++ b/rel/files/bin/pleroma_ctl @@ -78,19 +78,14 @@ update() { RELEASE_ROOT=$(dirname "$SCRIPTPATH") uri="https://git.pleroma.social" - project_name="pleroma" - package_base="${uri}/api/packages/${project_name}/generic" - if [ -n "$FULL_URI" ]; then - full_uri="$FULL_URI" - else - project_branch="${BRANCH:-$(detect_branch)}" - flavour="${FLAVOUR:-$(detect_flavour)}" - full_uri="${package_base}"/pleroma-otp-"${project_branch}"-"${flavour}"/latest/pleroma.zip - fi + project_id="2" + project_branch="${BRANCH:-$(detect_branch)}" + flavour="${FLAVOUR:-$(detect_flavour)}" tmp="${TMP_DIR:-/tmp}" artifact="$tmp/pleroma.zip" + full_uri="${FULL_URI:-${uri}/api/v4/projects/${project_id}/jobs/artifacts/${project_branch}/download?job=${flavour}}" echo "Downloading the artifact from ${full_uri} to ${artifact}" - curl -fL "$full_uri" -o "${artifact}" + curl "$full_uri" -o "${artifact}" echo "Unpacking ${artifact} to ${tmp}" unzip -q "$artifact" -d "$tmp" echo "Copying files over to $RELEASE_ROOT" @@ -142,14 +137,7 @@ else SCRIPT=$(realpath "$0") SCRIPTPATH=$(dirname "$SCRIPT") - # HACK: Script arguments need to be sent as an array to Mix tasks, otherwise they will break (quoted) arguments with whitespace. - # Previously it was sent as string, which would get split on whitespace on the task side. - # Encode as Elixir binary literals to avoid string escaping and interpolation issues. - PREPARED_ARGS="" - for arg in "$@"; do - bytes=$(printf '%s' "$arg" | od -An -v -tu1 | tr -s '[:space:]' ',' | sed 's/^,//; s/,$//') - PREPARED_ARGS="$PREPARED_ARGS <<$bytes>>," - done + FULL_ARGS="$*" ACTION="$1" if [ $# -gt 0 ]; then @@ -166,8 +154,8 @@ else if [ "$ACTION" = "update" ]; then update "$@" elif [ "$ACTION" = "migrate" ] || [ "$ACTION" = "rollback" ] || [ "$ACTION" = "create" ] || [ "$ACTION $SUBACTION" = "instance gen" ] || [ "$PLEROMA_CTL_RPC_DISABLED" = true ]; then - "$SCRIPTPATH"/pleroma eval 'Pleroma.ReleaseTasks.run(['"${PREPARED_ARGS%%,}"'])' + "$SCRIPTPATH"/pleroma eval 'Pleroma.ReleaseTasks.run("'"$FULL_ARGS"'")' else - "$SCRIPTPATH"/pleroma rpc 'Pleroma.ReleaseTasks.run(['"${PREPARED_ARGS%%,}"'])' + "$SCRIPTPATH"/pleroma rpc 'Pleroma.ReleaseTasks.run("'"$FULL_ARGS"'")' fi fi diff --git a/test/mix/tasks/pleroma/config_test.exs b/test/mix/tasks/pleroma/config_test.exs index f672d8c13..ef1adc235 100644 --- a/test/mix/tasks/pleroma/config_test.exs +++ b/test/mix/tasks/pleroma/config_test.exs @@ -144,13 +144,7 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do quarantined_instances: [], managed_config: true, static_dir: "instance/static/", - allowed_post_formats: [ - "text/plain", - "text/html", - "text/markdown", - "text/bbcode", - "text/x.misskeymarkdown" - ], + allowed_post_formats: ["text/plain", "text/html", "text/markdown", "text/bbcode"], autofollowed_nicknames: [], max_pinned_statuses: 1, attachment_links: false, diff --git a/test/pleroma/pleroma_ctl_test.exs b/test/pleroma/pleroma_ctl_test.exs deleted file mode 100644 index d96396399..000000000 --- a/test/pleroma/pleroma_ctl_test.exs +++ /dev/null @@ -1,293 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2022 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.PleromaCtlTest do - use ExUnit.Case, async: false - - @pleroma_ctl Path.expand("../../rel/files/bin/pleroma_ctl", __DIR__) - - setup do - tmp_dir = - Path.join(System.tmp_dir!(), "pleroma_ctl_test_#{System.unique_integer([:positive])}") - - release_root = Path.join(tmp_dir, "release") - bin_dir = Path.join(release_root, "bin") - - File.mkdir_p!(bin_dir) - File.cp!(@pleroma_ctl, Path.join(bin_dir, "pleroma_ctl")) - File.chmod!(Path.join(bin_dir, "pleroma_ctl"), 0o755) - - on_exit(fn -> File.rm_rf!(tmp_dir) end) - - {:ok, tmp_dir: tmp_dir, release_root: release_root, bin_dir: bin_dir} - end - - test "update downloads branch-scoped latest OTP package", %{ - tmp_dir: tmp_dir, - bin_dir: bin_dir, - release_root: release_root - } do - stubs_dir = Path.join(tmp_dir, "stubs") - curl_args_path = Path.join(tmp_dir, "curl.args") - File.mkdir_p!(stubs_dir) - write_update_stubs(stubs_dir, curl_args_path, "unused", "glibc") - - update_tmp_dir = Path.join(tmp_dir, "update_tmp") - File.mkdir_p!(update_tmp_dir) - - {output, status} = - System.cmd( - Path.join(bin_dir, "pleroma_ctl"), - [ - "update", - "--branch", - "develop", - "--flavour", - "amd64", - "--tmp-dir", - update_tmp_dir, - "--no-rm" - ], - env: [{"PATH", stubs_dir <> ":" <> System.get_env("PATH", "")}], - stderr_to_stdout: true - ) - - assert status == 0, output - assert File.exists?(Path.join(release_root, "bin/marker")) - - assert ["-fL", url, "-o", artifact_path] = - curl_args_path - |> File.read!() - |> String.split("\n", trim: true) - - assert url == - "https://git.pleroma.social/api/packages/pleroma/generic/pleroma-otp-develop-amd64/latest/pleroma.zip" - - assert artifact_path == Path.join(update_tmp_dir, "pleroma.zip") - end - - test "update detects stable branch and local flavour", %{ - tmp_dir: tmp_dir, - bin_dir: bin_dir, - release_root: release_root - } do - stubs_dir = Path.join(tmp_dir, "stubs") - curl_args_path = Path.join(tmp_dir, "curl.args") - File.mkdir_p!(stubs_dir) - write_update_stubs(stubs_dir, curl_args_path, "x86_64", "glibc") - write_start_erl_data(release_root, "2.10.0") - - {output, status} = - System.cmd( - Path.join(bin_dir, "pleroma_ctl"), - ["update", "--tmp-dir", create_update_tmp_dir(tmp_dir), "--no-rm"], - env: [{"PATH", stubs_dir <> ":" <> System.get_env("PATH", "")}], - stderr_to_stdout: true - ) - - assert status == 0, output - - assert curl_url(curl_args_path) == - "https://git.pleroma.social/api/packages/pleroma/generic/pleroma-otp-stable-amd64/latest/pleroma.zip" - end - - test "update detects develop branch and musl arm flavour", %{ - tmp_dir: tmp_dir, - bin_dir: bin_dir, - release_root: release_root - } do - stubs_dir = Path.join(tmp_dir, "stubs") - curl_args_path = Path.join(tmp_dir, "curl.args") - File.mkdir_p!(stubs_dir) - write_update_stubs(stubs_dir, curl_args_path, "armv7l", "musl") - write_start_erl_data(release_root, "2.10.0.develop") - - {output, status} = - System.cmd( - Path.join(bin_dir, "pleroma_ctl"), - ["update", "--tmp-dir", create_update_tmp_dir(tmp_dir), "--no-rm"], - env: [{"PATH", stubs_dir <> ":" <> System.get_env("PATH", "")}], - stderr_to_stdout: true - ) - - assert status == 0, output - - assert curl_url(curl_args_path) == - "https://git.pleroma.social/api/packages/pleroma/generic/pleroma-otp-develop-arm-musl/latest/pleroma.zip" - end - - test "update with zip URL bypasses branch and flavour detection", %{ - tmp_dir: tmp_dir, - bin_dir: bin_dir, - release_root: release_root - } do - stubs_dir = Path.join(tmp_dir, "stubs") - curl_args_path = Path.join(tmp_dir, "curl.args") - File.mkdir_p!(stubs_dir) - write_update_stubs(stubs_dir, curl_args_path, "unsupported-arch", "unsupported-libc") - write_start_erl_data(release_root, "2.10.0.custombranch") - - custom_url = "https://example.test/custom.zip" - - {output, status} = - System.cmd( - Path.join(bin_dir, "pleroma_ctl"), - [ - "update", - "--zip-url", - custom_url, - "--tmp-dir", - create_update_tmp_dir(tmp_dir), - "--no-rm" - ], - env: [{"PATH", stubs_dir <> ":" <> System.get_env("PATH", "")}], - stderr_to_stdout: true - ) - - assert status == 0, output - assert curl_url(curl_args_path) == custom_url - end - - test "passes arguments with spaces and Elixir string metacharacters", %{ - tmp_dir: tmp_dir, - bin_dir: bin_dir - } do - capture_path = Path.join(tmp_dir, "captured_args") - eval_path = Path.join(tmp_dir, "pleroma_ctl_eval.exs") - - write_executable(Path.join(bin_dir, "pleroma"), """ - #!/bin/sh - { - printf '%s\n' 'defmodule Pleroma.ReleaseTasks do' - printf '%s\n' ' def run(args), do: File.write!(System.fetch_env!("PLEROMA_CTL_CAPTURE"), :erlang.term_to_binary(args))' - printf '%s\n' 'end' - printf '%s\n' "$2" - } > "$PLEROMA_CTL_EVAL_FILE" - - exec elixir "$PLEROMA_CTL_EVAL_FILE" - """) - - {output, status} = - System.cmd( - Path.join(bin_dir, "pleroma_ctl"), - [ - "user", - "", - "has space", - ~s(has "quote"), - ~s(has \\ backslash), - ~S(#{:not_interpolated}) - ], - env: [ - {"PLEROMA_CTL_CAPTURE", capture_path}, - {"PLEROMA_CTL_EVAL_FILE", eval_path} - ], - stderr_to_stdout: true - ) - - assert status == 0, output - - assert capture_path - |> File.read!() - |> :erlang.binary_to_term() == [ - "user", - "", - "has space", - ~s(has "quote"), - ~s(has \\ backslash), - ~S(#{:not_interpolated}) - ] - end - - defp write_executable(path, contents) do - File.write!(path, contents) - File.chmod!(path, 0o755) - end - - defp write_start_erl_data(release_root, version) do - releases_dir = Path.join(release_root, "releases") - File.mkdir_p!(releases_dir) - File.write!(Path.join(releases_dir, "start_erl.data"), "erts-15.0 #{version}\n") - end - - defp create_update_tmp_dir(tmp_dir) do - update_tmp_dir = Path.join(tmp_dir, "update_tmp") - File.mkdir_p!(update_tmp_dir) - update_tmp_dir - end - - defp write_update_stubs(stubs_dir, curl_args_path, arch, libc) do - write_executable(Path.join(stubs_dir, "curl"), """ - #!/bin/sh - printf '%s\n' "$@" > "#{curl_args_path}" - - while [ $# -gt 0 ]; do - case "$1" in - -o) - artifact="$2" - shift 2 - ;; - *) - shift - ;; - esac - done - - : > "$artifact" - """) - - write_executable(Path.join(stubs_dir, "unzip"), """ - #!/bin/sh - while [ $# -gt 0 ]; do - case "$1" in - -d) - dest="$2" - shift 2 - ;; - *) - shift - ;; - esac - done - - mkdir -p "$dest/release/bin" - printf 'marker' > "$dest/release/bin/marker" - """) - - write_executable(Path.join(stubs_dir, "uname"), """ - #!/bin/sh - printf '%s\n' '#{arch}' - """) - - write_executable(Path.join(stubs_dir, "getconf"), getconf_stub(libc)) - - write_executable(Path.join(stubs_dir, "ldd"), """ - #!/bin/sh - printf '%s\n' 'musl libc (mock)' - """) - end - - defp getconf_stub("glibc") do - """ - #!/bin/sh - printf '%s\n' 'glibc 2.40' - """ - end - - defp getconf_stub(_libc) do - """ - #!/bin/sh - exit 1 - """ - end - - defp curl_url(curl_args_path) do - ["-fL", url, "-o", _artifact_path] = - curl_args_path - |> File.read!() - |> String.split("\n", trim: true) - - url - end -end diff --git a/test/pleroma/user_test.exs b/test/pleroma/user_test.exs index de735cdb7..b2533e9f1 100644 --- a/test/pleroma/user_test.exs +++ b/test/pleroma/user_test.exs @@ -876,17 +876,17 @@ defmodule Pleroma.UserTest do describe "get_or_fetch/1 remote users with tld, while BE is running on a subdomain" do setup do: clear_config([Pleroma.Web.WebFinger, :update_nickname_on_user_fetch], true) - test "fetches a mastodon split-domain nickname" do - nickname = "a@mastodon.example" - {:ok, fetched_user} = User.get_or_fetch(nickname) + test "for mastodon" do + ap_id = "a@mastodon.example" + {:ok, fetched_user} = User.get_or_fetch(ap_id) assert fetched_user.ap_id == "https://sub.mastodon.example/users/a" assert fetched_user.nickname == "a@mastodon.example" end - test "fetches a pleroma split-domain nickname" do - nickname = "a@pleroma.example" - {:ok, fetched_user} = User.get_or_fetch(nickname) + test "for pleroma" do + ap_id = "a@pleroma.example" + {:ok, fetched_user} = User.get_or_fetch(ap_id) assert fetched_user.ap_id == "https://sub.pleroma.example/users/a" assert fetched_user.nickname == "a@pleroma.example" @@ -936,89 +936,6 @@ defmodule Pleroma.UserTest do assert fetched_user == "not found nonexistent" end - test "does not rename an existing remote actor from rogue WebFinger data" do - clear_config([Pleroma.Web.WebFinger, :update_nickname_on_user_fetch], true) - - actor_id = "https://legit-actor.example/users/alice" - - Tesla.Mock.mock(fn - %{url: "https://evil-webfinger.example/.well-known/host-meta"} -> - {:ok, %Tesla.Env{status: 404}} - - %{ - url: - "https://evil-webfinger.example/.well-known/webfinger?resource=acct:claimed@evil-webfinger.example" - } -> - Tesla.Mock.json(%{ - "subject" => "acct:claimed@evil-webfinger.example", - "links" => [ - %{ - "rel" => "self", - "type" => "application/activity+json", - "href" => actor_id - } - ] - }) - - %{url: ^actor_id} -> - {:ok, - %Tesla.Env{ - status: 200, - headers: [{"content-type", "application/activity+json"}], - body: - Jason.encode!(%{ - "id" => actor_id, - "type" => "Person", - "preferredUsername" => "alice", - "name" => "Alice", - "summary" => "", - "inbox" => "https://legit-actor.example/users/alice/inbox", - "outbox" => "https://legit-actor.example/users/alice/outbox", - "followers" => "https://legit-actor.example/users/alice/followers", - "following" => "https://legit-actor.example/users/alice/following" - }) - }} - - %{url: "https://legit-actor.example/.well-known/host-meta"} -> - {:ok, %Tesla.Env{status: 404}} - - %{ - url: - "https://legit-actor.example/.well-known/webfinger?resource=acct:alice@legit-actor.example" - } -> - Tesla.Mock.json(%{ - "subject" => "acct:alice@legit-actor.example", - "links" => [ - %{ - "rel" => "self", - "type" => "application/activity+json", - "href" => actor_id - } - ] - }) - end) - - assert {:error, {:webfinger_actor_mismatch, "claimed@evil-webfinger.example", ^actor_id}} = - ActivityPub.make_user_from_nickname("claimed@evil-webfinger.example") - - refute User.get_by_ap_id(actor_id) - refute User.get_by_nickname("claimed@evil-webfinger.example") - - orig_user = - insert(:user, - local: false, - nickname: "alice@legit-actor.example", - ap_id: actor_id - ) - - assert {:error, {:webfinger_actor_mismatch, "claimed@evil-webfinger.example", ^actor_id}} = - ActivityPub.make_user_from_nickname("claimed@evil-webfinger.example") - - assert {:error, _} = User.get_or_fetch_by_nickname("claimed@evil-webfinger.example") - assert User.get_by_id(orig_user.id).nickname == "alice@legit-actor.example" - refute User.get_by_nickname("claimed@evil-webfinger.example") - end - test "updates an existing user, if stale" do a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800) diff --git a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs index 3988c3912..d5947186f 100644 --- a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs @@ -19,7 +19,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do alias Pleroma.Web.CommonAPI alias Pleroma.Web.Endpoint alias Pleroma.Workers.ReceiverWorker - alias Pleroma.Workers.SignatureRetryWorker import Pleroma.Factory @@ -37,36 +36,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do setup do: clear_config([:instance, :federating], true) - defp assign_valid_signature_for_actor(conn, %User{ap_id: actor_id}) do - assign_valid_signature_for_actor(conn, actor_id) - end - - defp assign_valid_signature_for_actor(conn, actor) do - actor_id = Utils.get_ap_id(actor) - - conn - |> assign(:valid_signature, true) - |> put_req_header("signature", "keyId=\"#{actor_id}#main-key\"") - end - - defp expect_signature_retry_from(%User{} = signer) do - signer_json = UserView.render("user.json", %{user: signer}) |> Map.delete("featured") - - Tesla.Mock.mock(fn - %{url: url} when url == signer.ap_id -> - %Tesla.Env{ - status: 200, - body: Jason.encode!(signer_json), - headers: HttpRequestMock.activitypub_object_headers() - } - - env -> - apply(HttpRequestMock, :request, [env]) - end) - - Mox.expect(Pleroma.StubbedHTTPSignaturesMock, :validate_conn, fn _conn -> true end) - end - describe "/relay" do setup do: clear_config([:instance, :allow_relay]) @@ -719,7 +688,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do conn = conn - |> assign_valid_signature_for_actor(data["actor"]) + |> assign(:valid_signature, true) |> put_req_header("content-type", "application/activity+json") |> post("/inbox", data) @@ -747,7 +716,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do conn = conn - |> assign_valid_signature_for_actor(data["actor"]) + |> assign(:valid_signature, true) |> put_req_header("content-type", "application/activity+json") |> post("/inbox", data) @@ -757,199 +726,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do assert Activity.get_by_ap_id(data["id"]) end - test "does not create a forged post after failed signature retry", %{conn: conn} do - alice = insert(:user, local: false, ap_id: "https://one.com/users/alice") - bob = insert(:user, local: false, ap_id: "https://two.com/users/bob") - object_id = "https://two.com/objects/inbox-forged-note" - - data = %{ - "type" => "Create", - "actor" => bob.ap_id, - "id" => "https://two.com/activities/inbox-forged-create", - "context" => "https://two.com/contexts/inbox-forged-create", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "object" => %{ - "type" => "Note", - "id" => object_id, - "actor" => bob.ap_id, - "attributedTo" => bob.ap_id, - "context" => "https://two.com/contexts/inbox-forged-create", - "content" => "forged post", - "published" => "2024-07-25T13:33:31Z", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [] - } - } - - expect_signature_retry_from(alice) - - conn = - conn - |> assign(:valid_signature, false) - |> put_req_header("content-type", "application/activity+json") - |> put_req_header("signature", "keyId=\"https://one.com/users/alice#main-key\"") - |> post("/inbox", data) - - assert "ok" == json_response(conn, 200) - - assert [{:cancel, :actor_signature_mismatch}] = - ObanHelpers.perform(all_enqueued(worker: SignatureRetryWorker)) - - refute Activity.get_by_ap_id(data["id"]) - refute Object.get_by_ap_id(object_id) - end - - test "does not create a forged like after failed signature retry", %{conn: conn} do - alice = insert(:user, local: false, ap_id: "https://one.com/users/alice") - bob = insert(:user, local: false, ap_id: "https://two.com/users/bob") - note = insert(:note) - - data = %{ - "type" => "Like", - "actor" => bob.ap_id, - "id" => "https://two.com/activities/inbox-forged-like", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "object" => note.data["id"] - } - - expect_signature_retry_from(alice) - - conn = - conn - |> assign(:valid_signature, false) - |> put_req_header("content-type", "application/activity+json") - |> put_req_header("signature", "keyId=\"https://one.com/users/alice#main-key\"") - |> post("/inbox", data) - - assert "ok" == json_response(conn, 200) - - assert [{:cancel, :actor_signature_mismatch}] = - ObanHelpers.perform(all_enqueued(worker: SignatureRetryWorker)) - - refute Activity.get_by_ap_id(data["id"]) - end - - test "does not delete an object after failed signature retry", %{conn: conn} do - alice = insert(:user, local: false, ap_id: "https://one.com/users/alice") - bob = insert(:user, local: false, ap_id: "https://two.com/users/bob") - note = insert(:note) - object_id = note.data["id"] - - data = %{ - "type" => "Delete", - "actor" => bob.ap_id, - "id" => "https://two.com/activities/inbox-forged-delete", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "object" => object_id - } - - expect_signature_retry_from(alice) - - conn = - conn - |> assign(:valid_signature, false) - |> put_req_header("content-type", "application/activity+json") - |> put_req_header("signature", "keyId=\"https://one.com/users/alice#main-key\"") - |> post("/inbox", data) - - assert "ok" == json_response(conn, 200) - - assert [{:cancel, :actor_signature_mismatch}] = - ObanHelpers.perform(all_enqueued(worker: SignatureRetryWorker)) - - refute Activity.get_by_ap_id(data["id"]) - assert %Object{data: %{"type" => "Note"}} = Object.get_by_ap_id(object_id) - end - - test "does not create a forged post signed by a different actor", %{conn: conn} do - alice = insert(:user, local: false, ap_id: "https://one.com/users/alice") - bob = insert(:user, local: false, ap_id: "https://two.com/users/bob") - object_id = "https://two.com/objects/inbox-signed-forged-note" - - data = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "type" => "Create", - "actor" => bob.ap_id, - "id" => "https://two.com/activities/inbox-signed-forged-create", - "context" => "https://two.com/contexts/inbox-signed-forged-create", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "object" => %{ - "type" => "Note", - "id" => object_id, - "actor" => bob.ap_id, - "attributedTo" => bob.ap_id, - "context" => "https://two.com/contexts/inbox-signed-forged-create", - "content" => "forged post", - "published" => "2024-07-25T13:33:31Z", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [] - } - } - - expect_signature_retry_from(alice) - - conn = - conn - |> put_req_header("content-type", "application/activity+json") - |> put_req_header("date", "Thu, 25 Jul 2024 13:33:31 GMT") - |> put_req_header("digest", "SHA-256=fake-digest") - |> put_req_header( - "signature", - "keyId=\"#{alice.ap_id}#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"fake-signature\"" - ) - |> post("/inbox", data) - - assert conn.assigns.valid_signature == false - assert "ok" == json_response(conn, 200) - - assert [{:cancel, :actor_signature_mismatch}] = - ObanHelpers.perform(all_enqueued(worker: SignatureRetryWorker)) - - refute Activity.get_by_ap_id(data["id"]) - refute Object.get_by_ap_id(object_id) - end - - test "does not create a forged like signed by a different actor", %{conn: conn} do - alice = insert(:user, local: false, ap_id: "https://one.com/users/alice") - bob = insert(:user, local: false, ap_id: "https://two.com/users/bob") - note = insert(:note) - - data = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "type" => "Like", - "actor" => bob.ap_id, - "id" => "https://two.com/activities/inbox-signed-forged-like", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "object" => note.data["id"] - } - - expect_signature_retry_from(alice) - - conn = - conn - |> put_req_header("content-type", "application/activity+json") - |> put_req_header("date", "Thu, 25 Jul 2024 13:33:31 GMT") - |> put_req_header("digest", "SHA-256=fake-digest") - |> put_req_header( - "signature", - "keyId=\"#{alice.ap_id}#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"fake-signature\"" - ) - |> post("/inbox", data) - - assert conn.assigns.valid_signature == false - assert "ok" == json_response(conn, 200) - - assert [{:cancel, :actor_signature_mismatch}] = - ObanHelpers.perform(all_enqueued(worker: SignatureRetryWorker)) - - refute Activity.get_by_ap_id(data["id"]) - end - test "accept follow activity", %{conn: conn} do clear_config([:instance, :federating], true) relay = Relay.get_actor() @@ -966,7 +742,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do assert "ok" == conn - |> assign_valid_signature_for_actor(followed_relay) + |> assign(:valid_signature, true) |> put_req_header("content-type", "application/activity+json") |> post("/inbox", accept) |> json_response(200) @@ -1046,19 +822,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do test "Unknown activity types are discarded", %{conn: conn} do unknown_types = ["Poke", "Read", "Dazzle"] - actor = - insert(:user, local: false, ap_id: "https://unknown.mastodon.instance/users/somebody") - Enum.each(unknown_types, fn bad_type -> params = %{ "type" => bad_type, - "actor" => actor.ap_id + "actor" => "https://unknown.mastodon.instance/users/somebody" } |> Jason.encode!() conn - |> assign_valid_signature_for_actor(actor) + |> assign(:valid_signature, true) |> put_req_header("content-type", "application/activity+json") |> post("/inbox", params) |> json_response(400) @@ -1127,7 +900,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do assert "ok" == conn - |> assign_valid_signature_for_actor(data["actor"]) + |> assign(:valid_signature, true) |> put_req_header("content-type", "application/activity+json") |> post("/inbox", data) |> json_response(200) @@ -1148,7 +921,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do assert "ok" == conn - |> assign_valid_signature_for_actor(data["actor"]) + |> assign(:valid_signature, true) |> put_req_header("content-type", "application/activity+json") |> post("/inbox", data) |> json_response(200) @@ -1217,7 +990,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do assert "ok" == conn - |> assign_valid_signature_for_actor(data["actor"]) + |> assign(:valid_signature, true) |> put_req_header("content-type", "application/activity+json") |> post("/inbox", data) |> json_response(200) @@ -1236,7 +1009,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do assert "ok" == conn - |> assign_valid_signature_for_actor(data["actor"]) + |> assign(:valid_signature, true) |> put_req_header("content-type", "application/activity+json") |> post("/inbox", data) |> json_response(200) @@ -1267,7 +1040,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do conn = conn - |> assign_valid_signature_for_actor(data["actor"]) + |> assign(:valid_signature, true) |> put_req_header("content-type", "application/activity+json") |> post("/users/#{user.nickname}/inbox", data) @@ -1288,7 +1061,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do conn = conn - |> assign_valid_signature_for_actor(data["actor"]) + |> assign(:valid_signature, true) |> put_req_header("content-type", "application/activity+json") |> post("/users/#{user.nickname}/inbox", data) @@ -1309,7 +1082,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do conn = conn - |> assign_valid_signature_for_actor(data["actor"]) + |> assign(:valid_signature, true) |> put_req_header("content-type", "application/activity+json") |> post("/users/#{user.nickname}/inbox", data) @@ -1333,7 +1106,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do conn = conn - |> assign_valid_signature_for_actor(data["actor"]) + |> assign(:valid_signature, true) |> put_req_header("content-type", "application/activity+json") |> post("/users/#{user.nickname}/inbox", data) @@ -1360,7 +1133,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do conn = conn - |> assign_valid_signature_for_actor(data["actor"]) + |> assign(:valid_signature, true) |> put_req_header("content-type", "application/activity+json") |> post("/users/#{user.nickname}/inbox", data) @@ -1390,7 +1163,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do conn = conn - |> assign_valid_signature_for_actor(data["actor"]) + |> assign(:valid_signature, true) |> put_req_header("content-type", "application/activity+json") |> post("/users/#{recipient.nickname}/inbox", data) @@ -1455,7 +1228,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do } conn - |> assign_valid_signature_for_actor(data["actor"]) + |> assign(:valid_signature, true) |> put_req_header("content-type", "application/activity+json") |> post("/users/#{recipient.nickname}/inbox", data) |> json_response(200) @@ -1545,7 +1318,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do } conn - |> assign_valid_signature_for_actor(data["actor"]) + |> assign(:valid_signature, true) |> put_req_header("content-type", "application/activity+json") |> post("/users/#{reported_user.nickname}/inbox", data) |> json_response(200) @@ -1599,7 +1372,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do } conn - |> assign_valid_signature_for_actor(data["actor"]) + |> assign(:valid_signature, true) |> put_req_header("content-type", "application/activity+json") |> post("/users/#{reported_user.nickname}/inbox", data) |> json_response(200) @@ -1632,7 +1405,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do conn = conn - |> assign_valid_signature_for_actor(data["actor"]) + |> assign(:valid_signature, true) |> put_req_header("content-type", "application/activity+json") |> post("/users/#{user.nickname}/inbox", data) @@ -1655,7 +1428,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do conn = conn - |> assign_valid_signature_for_actor(data["actor"]) + |> assign(:valid_signature, true) |> put_req_header("content-type", "application/activity+json") |> post("/users/#{user.nickname}/inbox", data) @@ -1678,7 +1451,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do conn = conn - |> assign_valid_signature_for_actor(data["actor"]) + |> assign(:valid_signature, true) |> put_req_header("content-type", "application/activity+json") |> post("/users/#{user.nickname}/inbox", data) @@ -2798,8 +2571,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do setup do: clear_config([:media_proxy]) setup do: clear_config([Pleroma.Upload]) - # majic's libmagic port is unavailable on local Darwin runs; Linux CI still runs this test. - @tag :skip_darwin test "POST /api/ap/upload_media", %{conn: conn} do user = insert(:user) diff --git a/test/pleroma/web/activity_pub/object_validators/article_note_page_validator_test.exs b/test/pleroma/web/activity_pub/object_validators/article_note_page_validator_test.exs index bf9c70fb6..c32811c5b 100644 --- a/test/pleroma/web/activity_pub/object_validators/article_note_page_validator_test.exs +++ b/test/pleroma/web/activity_pub/object_validators/article_note_page_validator_test.exs @@ -149,171 +149,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidatorTest %{valid?: true} = ArticleNotePageValidator.cast_and_validate(note) end - test "a Misskey MFM note is rendered from source content" do - user = insert(:user, ap_id: "https://misskey.example/users/alice") - - note = %{ - "id" => "https://misskey.example/notes/1", - "type" => "Note", - "actor" => user.ap_id, - "attributedTo" => user.ap_id, - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "content" => "original content", - "context" => Utils.generate_context_id(), - "source" => %{ - "content" => "$[spin.speed=1s mfm goes here] ", - "mediaType" => "text/x.misskeymarkdown" - } - } - - %{valid?: true, changes: %{content: content, source: source}} = - ArticleNotePageValidator.cast_and_validate(note) - - assert source["mediaType"] == "text/x.misskeymarkdown" - assert content =~ ~s(class="mfm-spin") - assert content =~ ~s(data-mfm-speed="1s") - assert content =~ "mfm goes here" - refute content =~ "original content" - refute content =~ " "https://misskey.example/notes/3", - "type" => "Note", - "actor" => remote_user.ap_id, - "attributedTo" => remote_user.ap_id, - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "content" => "original content", - "context" => Utils.generate_context_id(), - "tag" => [ - %{ - "type" => "Mention", - "name" => "@local_user", - "href" => local_user.ap_id - }, - %{ - "type" => "Mention", - "name" => "@uncached", - "href" => "https://misskey.example/users/uncached" - } - ], - "source" => %{ - "content" => "@local_user @uncached $[spin hello]", - "mediaType" => "text/x.misskeymarkdown" - } - } - - %{valid?: true, changes: %{content: content}} = - ArticleNotePageValidator.cast_and_validate(note) - - assert content =~ local_user.ap_id - assert content =~ "@uncached" - end - - test "a Misskey MFM note drops oversized source content instead of parsing it" do - user = insert(:user, ap_id: "https://misskey.example/users/oversized") - - note = %{ - "id" => "https://misskey.example/notes/4", - "type" => "Note", - "actor" => user.ap_id, - "attributedTo" => user.ap_id, - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "content" => "safe fallback", - "context" => Utils.generate_context_id(), - "source" => %{ - "content" => String.duplicate("x", 5_001), - "mediaType" => "text/x.misskeymarkdown" - } - } - - %{valid?: true, changes: %{content: content, source: source}} = - ArticleNotePageValidator.cast_and_validate(note) - - assert content == "safe fallback" - refute Map.has_key?(source, "content") - end - - test "a note drops oversized non-MFM source content" do - user = insert(:user, ap_id: "https://example.com/users/source") - - note = %{ - "id" => "https://example.com/notes/1", - "type" => "Note", - "actor" => user.ap_id, - "attributedTo" => user.ap_id, - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "content" => "regular content", - "context" => Utils.generate_context_id(), - "source" => %{ - "content" => String.duplicate("x", 5_001), - "mediaType" => "text/markdown" - } - } - - %{valid?: true, changes: %{source: source}} = ArticleNotePageValidator.cast_and_validate(note) - - assert source == %{"mediaType" => "text/markdown"} - end - - test "a Misskey MFM note with legacy _misskey_content is rendered" do - user = insert(:user, ap_id: "https://misskey.example/users/legacy") - - note = %{ - "id" => "https://misskey.example/notes/5", - "type" => "Note", - "actor" => user.ap_id, - "attributedTo" => user.ap_id, - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "content" => "original content", - "context" => Utils.generate_context_id(), - "_misskey_content" => "$[spin legacy]" - } - - %{valid?: true, changes: %{content: content, source: source}} = - ArticleNotePageValidator.cast_and_validate(note) - - assert source == %{"content" => "$[spin legacy]", "mediaType" => "text/x.misskeymarkdown"} - assert content =~ ~s(class="mfm-spin") - assert content =~ "legacy" - end - - test "a Misskey MFM note with htmlMfm is scrubbed but not rendered from source content" do - user = insert(:user, ap_id: "https://misskey.example/users/bob") - - note = %{ - "id" => "https://misskey.example/notes/2", - "type" => "Note", - "actor" => user.ap_id, - "attributedTo" => user.ap_id, - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "content" => - "already rendered", - "htmlMfm" => true, - "context" => Utils.generate_context_id(), - "source" => %{ - "content" => String.duplicate("x", 5_001), - "mediaType" => "text/x.misskeymarkdown" - } - } - - %{valid?: true, changes: %{content: content, htmlMfm: true, source: source}} = - ArticleNotePageValidator.cast_and_validate(note) - - assert content == "already renderedalert('xss')" - refute Map.has_key?(source, "content") - end - test "a Note with validated likes collection validates" do insert(:user, ap_id: "https://pol.social/users/mkljczk") diff --git a/test/pleroma/web/activity_pub/object_validators/update_handling_test.exs b/test/pleroma/web/activity_pub/object_validators/update_handling_test.exs index 9dec315b3..347c5b578 100644 --- a/test/pleroma/web/activity_pub/object_validators/update_handling_test.exs +++ b/test/pleroma/web/activity_pub/object_validators/update_handling_test.exs @@ -29,7 +29,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateHandlingTest do assert {:ok, _update, []} = ObjectValidator.validate(valid_update, []) end - test "returns an error if the object can't be updated by the actor (different domain)", %{ + test "returns an error if the object can't be updated by the actor", %{ valid_update: valid_update } do other_user = insert(:user, local: false) @@ -41,72 +41,27 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateHandlingTest do assert {:error, _cng} = ObjectValidator.validate(update, []) end - test "returns an error if the object can't be updated by the actor (same domain)", %{ - user: user, + test "validates as long as the object is same-origin with the actor", %{ valid_update: valid_update } do - user_ap_id = user.ap_id - user_domain = URI.parse(user_ap_id).host - other_user = insert(:user, local: false, domain: user_domain) + other_user = insert(:user) update = valid_update |> Map.put("actor", other_user.ap_id) - assert {:error, _cng} = ObjectValidator.validate(update, []) + assert {:ok, _update, []} = ObjectValidator.validate(update, []) end - test "validates if the object is not of an Actor type", %{user: user} do - note = insert(:note, user: user) + test "validates if the object is not of an Actor type" do + note = insert(:note) updated_note = note.data |> Map.put("content", "edited content") + other_user = insert(:user) - {:ok, update, _} = Builder.update(user, updated_note) + {:ok, update, _} = Builder.update(other_user, updated_note) assert {:ok, _update, _} = ObjectValidator.validate(update, []) end - - test "returns an error if the remote update target is unknown" do - remote_user = insert(:user, local: false, ap_id: "https://example.com/users/alice") - - update = %{ - "type" => "Update", - "actor" => remote_user.ap_id, - "id" => "https://example.com/activities/update-unknown-object", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "object" => %{ - "type" => "Note", - "id" => "https://example.com/objects/unknown", - "actor" => remote_user.ap_id, - "content" => "edited content", - "published" => "2024-07-25T13:33:31Z", - "updated" => "2024-07-25T13:34:31Z", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [] - } - } - - assert {:error, %Ecto.Changeset{} = cng} = ObjectValidator.validate(update, local: false) - refute cng.valid? - assert Keyword.has_key?(cng.errors, :object) - end - - test "returns an error if the remote update target IRI is unknown" do - remote_user = insert(:user, local: false, ap_id: "https://example.com/users/alice") - - update = %{ - "type" => "Update", - "actor" => remote_user.ap_id, - "id" => "https://example.com/activities/update-unknown-object-iri", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "object" => "https://example.com/objects/unknown-iri" - } - - assert {:error, %Ecto.Changeset{} = cng} = ObjectValidator.validate(update, local: false) - refute cng.valid? - assert Keyword.has_key?(cng.errors, :object) - end end describe "update note" do diff --git a/test/pleroma/web/activity_pub/utils_test.exs b/test/pleroma/web/activity_pub/utils_test.exs index 93234a015..3b77f0867 100644 --- a/test/pleroma/web/activity_pub/utils_test.exs +++ b/test/pleroma/web/activity_pub/utils_test.exs @@ -180,8 +180,7 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do "https://www.w3.org/ns/activitystreams", "http://localhost:4001/schemas/litepub-0.1.jsonld", %{ - "@language" => "und", - "htmlMfm" => "https://w3id.org/fep/c16b#htmlMfm" + "@language" => "und" } ] } @@ -193,8 +192,7 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do "https://www.w3.org/ns/activitystreams", "http://localhost:4001/schemas/litepub-0.1.jsonld", %{ - "@language" => "pl", - "htmlMfm" => "https://w3id.org/fep/c16b#htmlMfm" + "@language" => "pl" } ] } diff --git a/test/pleroma/web/common_api_test.exs b/test/pleroma/web/common_api_test.exs index ea1795c0b..017fac696 100644 --- a/test/pleroma/web/common_api_test.exs +++ b/test/pleroma/web/common_api_test.exs @@ -709,47 +709,6 @@ defmodule Pleroma.Web.CommonAPITest do assert object.data["source"]["content"] == post end - test "it renders MFM posts and marks their ActivityPub representation" do - user = insert(:user) - - post = "

$[spin.speed=1s 13:37]

" - - {:ok, activity} = - CommonAPI.post(user, %{ - status: post, - content_type: "text/x.misskeymarkdown" - }) - - object = Object.normalize(activity, fetch: false) - - assert object.data["htmlMfm"] == true - - assert object.data["source"] == %{ - "content" => post, - "mediaType" => "text/x.misskeymarkdown" - } - - assert object.data["content"] =~ ~s(class="mfm-spin") - assert object.data["content"] =~ ~s(data-mfm-speed="1s") - assert object.data["content"] =~ "13:37" - refute object.data["content"] =~ "scrub-this" - end - - test "it falls back safely for malformed MFM" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "$[spin.speed=1s=boom malformed]", - content_type: "text/x.misskeymarkdown" - }) - - object = Object.normalize(activity, fetch: false) - - refute object.data["content"] =~ ~s(class="mfm-spin") - assert object.data["content"] =~ "malformed" - end - test "it does not allow replies to direct messages that are not direct messages themselves" do user = insert(:user) diff --git a/test/pleroma/web/mastodon_api/views/poll_view_test.exs b/test/pleroma/web/mastodon_api/views/poll_view_test.exs index 6cb5934de..16281393d 100644 --- a/test/pleroma/web/mastodon_api/views/poll_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/poll_view_test.exs @@ -180,179 +180,4 @@ defmodule Pleroma.Web.MastodonAPI.PollViewTest do assert result[:pleroma][:non_anonymous] == true end - - test "prefers votersCount over voters list when both are present" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "Which flavor?", - poll: %{options: ["chocolate", "vanilla"], expires_in: 20} - }) - - object = Object.normalize(activity, fetch: false) - - voter = insert(:user) - {:ok, _, object} = CommonAPI.vote(object, voter, [0]) - - assert object.data["votersCount"] == 1 - assert length(object.data["voters"]) == 1 - - object = %{ - object - | data: Map.put(object.data, "votersCount", 42) - } - - result = PollView.render("show.json", %{object: object}) - - assert result[:voters_count] == 42 - end - - test "falls back to voters list when votersCount is absent" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "Which flavor?", - poll: %{options: ["chocolate", "vanilla"], expires_in: 20} - }) - - object = Object.normalize(activity, fetch: false) - - voter = insert(:user) - {:ok, _, object} = CommonAPI.vote(object, voter, [0]) - - assert length(object.data["voters"]) == 1 - - data = Map.delete(object.data, "votersCount") - object = %{object | data: data} - - result = PollView.render("show.json", %{object: object}) - - assert result[:voters_count] == 1 - end - - test "returns 0 when both votersCount and voters are absent" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "Which flavor?", - poll: %{options: ["chocolate", "vanilla"], expires_in: 20} - }) - - object = Object.normalize(activity, fetch: false) - - data = - object.data - |> Map.delete("votersCount") - |> Map.delete("voters") - - object = %{object | data: data} - - result = PollView.render("show.json", %{object: object}) - - assert result[:voters_count] == 0 - end - - test "returns 0 when voters list is empty" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "Which flavor?", - poll: %{options: ["chocolate", "vanilla"], expires_in: 20} - }) - - object = Object.normalize(activity, fetch: false) - - data = - object.data - |> Map.delete("votersCount") - |> Map.put("voters", []) - - object = %{object | data: data} - - result = PollView.render("show.json", %{object: object}) - - assert result[:voters_count] == 0 - end - - test "does not inflate votersCount when same voter picks multiple options" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "Pick several", - poll: %{options: ["a", "b", "c"], expires_in: 20, multiple: true} - }) - - object = Object.normalize(activity, fetch: false) - - voter = insert(:user) - {:ok, _, object} = CommonAPI.vote(object, voter, [0, 2]) - - assert object.data["votersCount"] == 1 - assert length(object.data["voters"]) == 1 - end - - test "preserves votersCount from remote source when existing voter picks another option" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "Pick several", - poll: %{options: ["a", "b"], expires_in: 20, multiple: true} - }) - - object = Object.normalize(activity, fetch: false) - - voter = insert(:user) - {:ok, _, object} = CommonAPI.vote(object, voter, [0, 1]) - - object = %{object | data: Map.put(object.data, "votersCount", 14)} - - result = PollView.render("show.json", %{object: object}) - - assert result[:voters_count] == 14 - end - - test "returns 0 when votersCount is explicitly 0" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "Pick one", - poll: %{options: ["a", "b"], expires_in: 20} - }) - - object = Object.normalize(activity, fetch: false) - - object = %{object | data: Map.put(object.data, "votersCount", 0)} - - result = PollView.render("show.json", %{object: object}) - - assert result[:voters_count] == 0 - end - - test "falls back to voters list when votersCount is nil" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "Pick one", - poll: %{options: ["a", "b"], expires_in: 20} - }) - - object = Object.normalize(activity, fetch: false) - - voter = insert(:user) - {:ok, _, object} = CommonAPI.vote(object, voter, [0]) - - object = %{object | data: Map.put(object.data, "votersCount", nil)} - - result = PollView.render("show.json", %{object: object}) - - assert result[:voters_count] == length(object.data["voters"]) - end end diff --git a/test/pleroma/web/plugs/mapped_signature_to_identity_plug_test.exs b/test/pleroma/web/plugs/mapped_signature_to_identity_plug_test.exs index df713762c..33eff1bc5 100644 --- a/test/pleroma/web/plugs/mapped_signature_to_identity_plug_test.exs +++ b/test/pleroma/web/plugs/mapped_signature_to_identity_plug_test.exs @@ -47,27 +47,13 @@ defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlugTest do assert %{valid_signature: false} == conn.assigns end + @tag skip: "known breakage; the testsuite presently depends on it" test "it considers a mapped identity to be invalid when the identity cannot be found" do - actor = "http://niu.moe/users/rye" - conn = - build_conn(:post, "/doesntmattter", %{"actor" => actor}) - |> set_signature(actor) + build_conn(:post, "/doesntmattter", %{"actor" => "http://mastodon.example.org/users/admin"}) + |> set_signature("http://niu.moe/users/rye") |> MappedSignatureToIdentityPlug.call(%{}) - assert conn.assigns.valid_signature == false - refute Map.has_key?(conn.assigns, :user) - end - - test "it considers a mapped identity to be invalid when embedded actor identity cannot be found" do - actor = "http://niu.moe/users/rye" - - conn = - build_conn(:post, "/doesntmattter", %{"actor" => %{"id" => actor}}) - |> set_signature(actor) - |> MappedSignatureToIdentityPlug.call(%{}) - - assert conn.assigns.valid_signature == false - refute Map.has_key?(conn.assigns, :user) + assert %{valid_signature: false} == conn.assigns end end diff --git a/test/pleroma/workers/receiver_worker_test.exs b/test/pleroma/workers/receiver_worker_test.exs index ea05f38f1..12abc1a27 100644 --- a/test/pleroma/workers/receiver_worker_test.exs +++ b/test/pleroma/workers/receiver_worker_test.exs @@ -11,27 +11,9 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do alias Pleroma.User alias Pleroma.Web.CommonAPI + alias Pleroma.Web.Federator alias Pleroma.Workers.ReceiverWorker - defp signature_headers_for(%User{} = signer) do - [ - {"host", "local.test"}, - {"date", "Thu, 25 Jul 2024 13:33:31 GMT"}, - {"digest", "SHA-256=fake-digest"}, - {"content-type", "application/activity+json"}, - { - "signature", - "keyId=\"#{signer.ap_id}#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"fake-signature\"" - } - ] - end - - defp perform_incoming(params) do - ReceiverWorker.perform(%Oban.Job{ - args: %{"op" => "incoming_ap_doc", "params" => params} - }) - end - test "it does not retry MRF reject" do params = insert(:note).data @@ -99,7 +81,16 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do insert(:note_activity).data |> Map.put("actor", "https://springfield.social/users/bart") - assert {:cancel, {:error, :forbidden}} = perform_incoming(params) + {:ok, oban_job} = + Federator.incoming_ap_doc(%{ + method: "POST", + req_headers: [], + request_path: "/inbox", + params: params, + query_string: "" + }) + + assert {:cancel, {:error, :forbidden}} = ReceiverWorker.perform(oban_job) end test "when request returns a 404" do @@ -107,7 +98,16 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do insert(:note_activity).data |> Map.put("actor", "https://springfield.social/users/troymcclure") - assert {:cancel, {:error, :not_found}} = perform_incoming(params) + {:ok, oban_job} = + Federator.incoming_ap_doc(%{ + method: "POST", + req_headers: [], + request_path: "/inbox", + params: params, + query_string: "" + }) + + assert {:cancel, {:error, :not_found}} = ReceiverWorker.perform(oban_job) end test "when request returns a 410" do @@ -115,7 +115,16 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do insert(:note_activity).data |> Map.put("actor", "https://springfield.social/users/hankscorpio") - assert {:cancel, {:error, :not_found}} = perform_incoming(params) + {:ok, oban_job} = + Federator.incoming_ap_doc(%{ + method: "POST", + req_headers: [], + request_path: "/inbox", + params: params, + query_string: "" + }) + + assert {:cancel, {:error, :not_found}} = ReceiverWorker.perform(oban_job) end test "when user account is disabled" do @@ -129,16 +138,86 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do {:ok, %User{}} = User.set_activation(user, false) - assert {:cancel, {:user_active, false}} = perform_incoming(params) + {:ok, oban_job} = + Federator.incoming_ap_doc(%{ + method: "POST", + req_headers: [], + request_path: "/inbox", + params: params, + query_string: "" + }) + + assert {:cancel, {:user_active, false}} = ReceiverWorker.perform(oban_job) end end + test "it can validate the signature" do + Tesla.Mock.mock(fn + %{url: "https://phpc.social/users/denniskoch"} -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/denniskoch.json"), + headers: [{"content-type", "application/activity+json"}] + } + + %{url: "https://phpc.social/users/denniskoch/collections/featured"} -> + %Tesla.Env{ + status: 200, + headers: [{"content-type", "application/activity+json"}], + body: + File.read!("test/fixtures/users_mock/masto_featured.json") + |> String.replace("{{domain}}", "phpc.social") + |> String.replace("{{nickname}}", "denniskoch") + } + end) + + params = + File.read!("test/fixtures/receiver_worker_signature_activity.json") |> Jason.decode!() + + req_headers = [ + ["accept-encoding", "gzip"], + ["content-length", "5184"], + ["content-type", "application/activity+json"], + ["date", "Thu, 25 Jul 2024 13:33:31 GMT"], + ["digest", "SHA-256=ouge/6HP2/QryG6F3JNtZ6vzs/hSwMk67xdxe87eH7A="], + ["host", "bikeshed.party"], + [ + "signature", + "keyId=\"https://mastodon.social/users/bastianallgeier#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"ymE3vn5Iw50N6ukSp8oIuXJB5SBjGAGjBasdTDvn+ahZIzq2SIJfmVCsIIzyqIROnhWyQoTbavTclVojEqdaeOx+Ejz2wBnRBmhz5oemJLk4RnnCH0lwMWyzeY98YAvxi9Rq57Gojuv/1lBqyGa+rDzynyJpAMyFk17XIZpjMKuTNMCbjMDy76ILHqArykAIL/v1zxkgwxY/+ELzxqMpNqtZ+kQ29znNMUBB3eVZ/mNAHAz6o33Y9VKxM2jw+08vtuIZOusXyiHbRiaj2g5HtN2WBUw1MzzfRfHF2/yy7rcipobeoyk5RvP5SyHV3WrIeZ3iyoNfmv33y8fxllF0EA==\"" + ], + [ + "user-agent", + "http.rb/5.2.0 (Mastodon/4.3.0-nightly.2024-07-25; +https://mastodon.social/)" + ] + ] + + {:ok, oban_job} = + Federator.incoming_ap_doc(%{ + method: "POST", + req_headers: req_headers, + request_path: "/inbox", + params: params, + query_string: "" + }) + + assert {:ok, %Pleroma.Activity{}} = ReceiverWorker.perform(oban_job) + end + test "cancels due to origin containment" do params = insert(:note_activity).data |> Map.put("id", "https://notorigindomain.com/activity") - assert {:cancel, :origin_containment_failed} = perform_incoming(params) + {:ok, oban_job} = + Federator.incoming_ap_doc(%{ + method: "POST", + req_headers: [], + request_path: "/inbox", + params: params, + query_string: "" + }) + + assert {:cancel, :origin_containment_failed} = ReceiverWorker.perform(oban_job) end test "canceled due to deleted object" do @@ -154,114 +233,16 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do } end) - assert {:cancel, _} = perform_incoming(params) - end + {:ok, oban_job} = + Federator.incoming_ap_doc(%{ + method: "POST", + req_headers: [], + request_path: "/inbox", + params: params, + query_string: "" + }) - test "delegates legacy failed-signature metadata jobs instead of processing them as trusted" do - alice = insert(:user, local: false, ap_id: "https://one.com/users/alice") - bob = insert(:user, local: false, ap_id: "https://two.com/users/bob") - object_id = "https://two.com/objects/legacy-forged-note" - - create = %{ - "type" => "Create", - "actor" => bob.ap_id, - "id" => "https://two.com/activities/legacy-forged-create", - "context" => "https://two.com/contexts/legacy-forged-create", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "object" => %{ - "type" => "Note", - "id" => object_id, - "actor" => bob.ap_id, - "attributedTo" => bob.ap_id, - "context" => "https://two.com/contexts/legacy-forged-create", - "content" => "forged post", - "published" => "2024-07-25T13:33:31Z", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [] - } - } - - assert {:cancel, :actor_signature_mismatch} = - ReceiverWorker.perform(%Oban.Job{ - args: %{ - "op" => "incoming_ap_doc", - "method" => "POST", - "params" => create, - "req_headers" => signature_headers_for(alice), - "request_path" => "/inbox", - "query_string" => "" - } - }) - - refute Pleroma.Activity.get_by_ap_id(create["id"]) - refute Pleroma.Object.get_by_ap_id(object_id) - end - - test "fails closed for the old persisted failed-signature job shape" do - alice = insert(:user, local: false, ap_id: "https://one.com/users/alice") - bob = insert(:user, local: false, ap_id: "https://two.com/users/bob") - object_id = "https://two.com/objects/old-shape-forged-note" - - create = %{ - "type" => "Create", - "actor" => bob.ap_id, - "id" => "https://two.com/activities/old-shape-forged-create", - "context" => "https://two.com/contexts/old-shape-forged-create", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "object" => %{ - "type" => "Note", - "id" => object_id, - "actor" => bob.ap_id, - "attributedTo" => bob.ap_id, - "context" => "https://two.com/contexts/old-shape-forged-create", - "content" => "forged post", - "published" => "2024-07-25T13:33:31Z", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [] - } - } - - assert {:cancel, :missing_signature_retry_metadata} = - ReceiverWorker.perform(%Oban.Job{ - args: %{ - "op" => "incoming_ap_doc", - "params" => create, - "req_headers" => signature_headers_for(alice), - "timeout" => 20_000 - } - }) - - refute Pleroma.Activity.get_by_ap_id(create["id"]) - refute Pleroma.Object.get_by_ap_id(object_id) - end - - test "fails closed for legacy retry jobs missing one metadata field" do - alice = insert(:user, local: false, ap_id: "https://one.com/users/alice") - params = insert(:note_activity).data - - assert {:cancel, :missing_signature_retry_metadata} = - ReceiverWorker.perform(%Oban.Job{ - args: %{ - "op" => "incoming_ap_doc", - "method" => "POST", - "params" => params, - "req_headers" => signature_headers_for(alice), - "request_path" => "/inbox" - } - }) - end - - test "fails closed for malformed legacy metadata jobs without params" do - assert {:cancel, :missing_signature_retry_metadata} = - ReceiverWorker.perform(%Oban.Job{ - args: %{ - "op" => "incoming_ap_doc", - "req_headers" => [], - "timeout" => 20_000 - } - }) + assert {:cancel, _} = ReceiverWorker.perform(oban_job) end describe "Server reachability:" do diff --git a/test/pleroma/workers/signature_retry_worker_test.exs b/test/pleroma/workers/signature_retry_worker_test.exs deleted file mode 100644 index 94dd5f6c1..000000000 --- a/test/pleroma/workers/signature_retry_worker_test.exs +++ /dev/null @@ -1,574 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2022 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Workers.SignatureRetryWorkerTest do - use Pleroma.DataCase, async: false - use Oban.Testing, repo: Pleroma.Repo - - import ExUnit.CaptureLog - import Pleroma.Factory - - @moduletag capture_log: true - - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.Signature - alias Pleroma.User - alias Pleroma.Web.ActivityPub.UserView - alias Pleroma.Web.Federator - alias Pleroma.Workers.SignatureRetryWorker - - defp signature_headers_for(%User{} = signer) do - [ - {"host", "local.test"}, - {"date", "Thu, 25 Jul 2024 13:33:31 GMT"}, - {"digest", "SHA-256=fake-digest"}, - {"content-type", "application/activity+json"}, - { - "signature", - "keyId=\"#{signer.ap_id}#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"fake-signature\"" - } - ] - end - - defp stub_actor_fetch(%User{} = signer) do - signer_json = UserView.render("user.json", %{user: signer}) |> Map.delete("featured") - - Tesla.Mock.mock(fn - %{url: url} when url == signer.ap_id -> - %Tesla.Env{ - status: 200, - body: Jason.encode!(signer_json), - headers: HttpRequestMock.activitypub_object_headers() - } - end) - end - - defp expect_signature_from(%User{} = signer) do - stub_actor_fetch(signer) - Mox.expect(Pleroma.StubbedHTTPSignaturesMock, :validate_conn, fn _conn -> true end) - end - - defp enqueue_failed_signature(params, signer) do - Federator.incoming_failed_signature_ap_doc(%{ - method: "POST", - req_headers: signature_headers_for(signer), - request_path: "/inbox", - params: params, - query_string: "" - }) - end - - defp failed_signature_job(params, req_headers, opts \\ []) do - %Oban.Job{ - args: %{ - "op" => "incoming_failed_signature_ap_doc", - "method" => Keyword.get(opts, :method, "POST"), - "req_headers" => req_headers, - "request_path" => Keyword.get(opts, :request_path, "/inbox"), - "params" => params, - "query_string" => Keyword.get(opts, :query_string, "") - } - } - end - - defp assert_mismatched_signature_cancelled(params, signer) do - assert {:ok, oban_job} = enqueue_failed_signature(params, signer) - - capture_log([level: :warning], fn -> - assert {:cancel, :actor_signature_mismatch} = SignatureRetryWorker.perform(oban_job) - end) - end - - test "Federator preserves request metadata for failed-signature retry jobs" do - params = insert(:note_activity).data - - req_headers = [ - {"host", "local.test"}, - {"signature", "keyId=\"https://one.com/users/alice#main-key\""} - ] - - assert {:ok, oban_job} = - Federator.incoming_failed_signature_ap_doc(%{ - method: "POST", - req_headers: req_headers, - request_path: "/inbox", - params: params, - query_string: "foo=bar" - }) - - assert oban_job.worker == "Pleroma.Workers.SignatureRetryWorker" - - assert %{ - "op" => "incoming_failed_signature_ap_doc", - "method" => "POST", - "req_headers" => ^req_headers, - "request_path" => "/inbox", - "params" => ^params, - "query_string" => "foo=bar" - } = oban_job.args - end - - test "cancels retry jobs without request metadata" do - params = insert(:note_activity).data - - log = - capture_log([level: :warning], fn -> - assert {:cancel, :missing_signature_retry_metadata} = - SignatureRetryWorker.perform(%Oban.Job{ - args: %{"op" => "incoming_failed_signature_ap_doc", "params" => params} - }) - end) - - assert log =~ "Failed-signature inbox retry rejected" - assert log =~ "reason=:missing_signature_retry_metadata" - assert log =~ "payload_actor=#{inspect(params["actor"])}" - assert log =~ "activity_id=#{inspect(params["id"])}" - assert log =~ "type=#{inspect(params["type"])}" - assert log =~ "request_path=nil" - end - - test "cancels retry jobs with malformed serialized request headers" do - params = insert(:note_activity).data - - log = - capture_log([level: :warning], fn -> - assert {:cancel, :invalid_signature_retry_metadata} = - SignatureRetryWorker.perform(failed_signature_job(params, [["signature"]])) - end) - - assert log =~ "Failed-signature inbox retry rejected" - assert log =~ "reason=:invalid_signature_retry_metadata" - assert log =~ "signature_actor=nil" - assert log =~ "request_path=\"/inbox\"" - end - - test "cancels retry jobs without a signature header" do - alice = insert(:user, local: false, ap_id: "https://one.com/users/alice") - params = insert(:note_activity, user: alice).data - - log = - capture_log([level: :warning], fn -> - assert {:cancel, :invalid_signature} = - SignatureRetryWorker.perform( - failed_signature_job(params, [{"host", "local.test"}]) - ) - end) - - assert log =~ "Failed-signature inbox retry rejected" - assert log =~ "reason=:invalid_signature" - assert log =~ "payload_actor=#{inspect(params["actor"])}" - assert log =~ "signature_actor=nil" - assert log =~ "request_path=\"/inbox\"" - end - - test "cancels missing signature before fetching an unavailable payload actor" do - params = - insert(:note_activity).data - |> Map.put("actor", "https://unavailable.example/users/bob") - - assert {:cancel, :invalid_signature} = - SignatureRetryWorker.perform(failed_signature_job(params, [{"host", "local.test"}])) - end - - test "cancels signer mismatch before fetching an unavailable payload actor" do - alice = insert(:user, local: false, ap_id: "https://one.com/users/alice") - - params = - insert(:note_activity).data - |> Map.put("actor", "https://unavailable.example/users/bob") - - assert {:cancel, :actor_signature_mismatch} = - SignatureRetryWorker.perform( - failed_signature_job(params, signature_headers_for(alice)) - ) - end - - test "cancels retry jobs with a signature header without keyId" do - alice = insert(:user, local: false, ap_id: "https://one.com/users/alice") - params = insert(:note_activity, user: alice).data - - req_headers = [{"signature", "algorithm=\"rsa-sha256\",signature=\"fake-signature\""}] - - assert {:cancel, :invalid_signature} = - SignatureRetryWorker.perform(failed_signature_job(params, req_headers)) - end - - test "cancels retry jobs with an unparsable signature keyId" do - alice = insert(:user, local: false, ap_id: "https://one.com/users/alice") - params = insert(:note_activity, user: alice).data - req_headers = [{"signature", "keyId=\"not an activitypub id\",signature=\"fake-signature\""}] - - assert {:cancel, :invalid_signature} = - SignatureRetryWorker.perform(failed_signature_job(params, req_headers)) - end - - test "cancels when the refetched key still cannot validate the signature" do - alice = insert(:user, local: false, ap_id: "https://one.com/users/alice") - - create = %{ - "type" => "Create", - "actor" => alice.ap_id, - "id" => "https://one.com/activities/invalid-signature-create", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "object" => %{ - "type" => "Note", - "id" => "https://one.com/objects/invalid-signature-note", - "actor" => alice.ap_id, - "attributedTo" => alice.ap_id, - "content" => "forged post", - "published" => "2024-07-25T13:33:31Z", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [] - } - } - - stub_actor_fetch(alice) - - assert {:ok, oban_job} = enqueue_failed_signature(create, alice) - - log = - capture_log([level: :warning], fn -> - assert {:cancel, :invalid_signature} = SignatureRetryWorker.perform(oban_job) - end) - - assert log =~ "Failed-signature inbox retry rejected" - assert log =~ "reason=:invalid_signature" - assert log =~ "payload_actor=\"https://one.com/users/alice\"" - assert log =~ "signature_actor=\"https://one.com/users/alice\"" - assert log =~ "activity_id=\"https://one.com/activities/invalid-signature-create\"" - assert log =~ "type=\"Create\"" - assert log =~ "request_path=\"/inbox\"" - - refute Activity.get_by_ap_id(create["id"]) - end - - test "processes the activity after refetching a valid matching signature" do - alice = insert(:user, local: false, ap_id: "https://one.com/users/alice") - - create = %{ - "type" => "Create", - "actor" => alice.ap_id, - "id" => "https://one.com/activities/valid-signature-create", - "context" => "https://one.com/contexts/valid-signature-create", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "object" => %{ - "type" => "Note", - "id" => "https://one.com/objects/valid-signature-note", - "actor" => alice.ap_id, - "attributedTo" => alice.ap_id, - "context" => "https://one.com/contexts/valid-signature-create", - "content" => "valid post", - "published" => "2024-07-25T13:33:31Z", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [] - } - } - - expect_signature_from(alice) - - assert {:ok, oban_job} = enqueue_failed_signature(create, alice) - assert {:ok, %Activity{}} = SignatureRetryWorker.perform(oban_job) - assert Activity.get_by_ap_id(create["id"]) - end - - test "processes the activity when a real signature validates with a query string" do - alice = insert(:user, local: false, ap_id: "https://one.com/users/alice") - - create = %{ - "type" => "Create", - "actor" => alice.ap_id, - "id" => "https://one.com/activities/valid-query-signature-create", - "context" => "https://one.com/contexts/valid-query-signature-create", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "object" => %{ - "type" => "Note", - "id" => "https://one.com/objects/valid-query-signature-note", - "actor" => alice.ap_id, - "attributedTo" => alice.ap_id, - "context" => "https://one.com/contexts/valid-query-signature-create", - "content" => "valid signed post", - "published" => "2024-07-25T13:33:31Z", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [] - } - } - - stub_actor_fetch(alice) - - date = "Thu, 25 Jul 2024 13:33:31 GMT" - digest = "SHA-256=fake-digest" - - signature = - Signature.sign(alice, %{ - "(request-target)" => "post /inbox?foo=bar", - "content-type" => "application/activity+json", - date: date, - digest: digest, - host: "local.test" - }) - - req_headers = [ - ["host", "local.test"], - ["date", date], - ["digest", digest], - ["content-type", "application/activity+json"], - ["signature", signature] - ] - - assert {:ok, %Activity{}} = - SignatureRetryWorker.perform( - failed_signature_job(create, req_headers, query_string: "foo=bar") - ) - - assert Activity.get_by_ap_id(create["id"]) - end - - test "cancels when signature actor does not match payload actor" do - alice = insert(:user, local: false, ap_id: "https://one.com/users/alice") - bob = insert(:user, local: false, ap_id: "https://two.com/users/bob") - - note = - insert(:note, - user: bob, - object_local: false, - data: %{"id" => "https://two.com/objects/malicious-update-note"} - ) - - update = %{ - "type" => "Update", - "actor" => bob.ap_id, - "id" => "https://two.com/activities/malicious-update", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "object" => note.data - } - - assert_mismatched_signature_cancelled(update, alice) - end - - test "cancels signature actor mismatch through Federator-created jobs" do - alice = insert(:user, local: false, ap_id: "https://one.com/users/alice") - bob = insert(:user, local: false, ap_id: "https://two.com/users/bob") - - note = - insert(:note, - user: bob, - object_local: false, - data: %{"id" => "https://two.com/objects/federator-malicious-note"} - ) - - update = %{ - "type" => "Update", - "actor" => bob.ap_id, - "id" => "https://two.com/activities/federator-malicious-update", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "object" => note.data - } - - assert_mismatched_signature_cancelled(update, alice) - end - - test "cancels signature actor mismatch before processing a forged Create" do - alice = insert(:user, local: false, ap_id: "https://one.com/users/alice") - bob = insert(:user, local: false, ap_id: "https://two.com/users/bob") - - create = %{ - "type" => "Create", - "actor" => bob.ap_id, - "id" => "https://two.com/activities/forged-create", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "object" => %{ - "type" => "Note", - "id" => "https://two.com/objects/forged-note", - "actor" => bob.ap_id, - "attributedTo" => bob.ap_id, - "content" => "forged post", - "published" => "2024-07-25T13:33:31Z", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [] - } - } - - assert_mismatched_signature_cancelled(create, alice) - end - - test "cancels signature actor mismatch when payload actor is embedded" do - alice = insert(:user, local: false, ap_id: "https://one.com/users/alice") - bob = insert(:user, local: false, ap_id: "https://two.com/users/bob") - - create = %{ - "type" => "Create", - "actor" => %{"id" => bob.ap_id}, - "id" => "https://two.com/activities/embedded-actor-forged-create", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "object" => %{ - "type" => "Note", - "id" => "https://two.com/objects/embedded-actor-forged-note", - "actor" => bob.ap_id, - "attributedTo" => bob.ap_id, - "content" => "forged post", - "published" => "2024-07-25T13:33:31Z", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [] - } - } - - assert_mismatched_signature_cancelled(create, alice) - end - - test "logs signature actor mismatch retry rejections" do - alice = insert(:user, local: false, ap_id: "https://one.com/users/alice") - bob = insert(:user, local: false, ap_id: "https://two.com/users/bob") - - create = %{ - "type" => "Create", - "actor" => bob.ap_id, - "id" => "https://two.com/activities/logged-forged-create", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "object" => %{ - "type" => "Note", - "id" => "https://two.com/objects/logged-forged-note", - "actor" => bob.ap_id, - "attributedTo" => bob.ap_id, - "content" => "forged post", - "published" => "2024-07-25T13:33:31Z", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [] - } - } - - log = assert_mismatched_signature_cancelled(create, alice) - - assert log =~ "Failed-signature inbox retry rejected" - assert log =~ "reason=:actor_signature_mismatch" - assert log =~ "payload_actor=\"https://two.com/users/bob\"" - assert log =~ "signature_actor=\"https://one.com/users/alice\"" - assert log =~ "activity_id=\"https://two.com/activities/logged-forged-create\"" - assert log =~ "type=\"Create\"" - assert log =~ "request_path=\"/inbox\"" - end - - test "cancels signature actor mismatch before actually creating a forged post" do - alice = insert(:user, local: false, ap_id: "https://one.com/users/alice") - bob = insert(:user, local: false, ap_id: "https://two.com/users/bob") - - object_id = "https://two.com/objects/actually-forged-note" - - create = %{ - "type" => "Create", - "actor" => bob.ap_id, - "id" => "https://two.com/activities/actually-forged-create", - "context" => "https://two.com/contexts/actually-forged-create", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "object" => %{ - "type" => "Note", - "id" => object_id, - "actor" => bob.ap_id, - "attributedTo" => bob.ap_id, - "context" => "https://two.com/contexts/actually-forged-create", - "content" => "forged post", - "published" => "2024-07-25T13:33:31Z", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [] - } - } - - assert_mismatched_signature_cancelled(create, alice) - refute Object.get_by_ap_id(object_id) - end - - test "cancels signature actor mismatch before processing a forged Like" do - alice = insert(:user, local: false, ap_id: "https://one.com/users/alice") - bob = insert(:user, local: false, ap_id: "https://two.com/users/bob") - note = insert(:note) - - like = %{ - "type" => "Like", - "actor" => bob.ap_id, - "id" => "https://two.com/activities/forged-like", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "object" => note.data["id"] - } - - assert_mismatched_signature_cancelled(like, alice) - end - - test "cancels signature actor mismatch before actually creating a forged Like" do - alice = insert(:user, local: false, ap_id: "https://one.com/users/alice") - bob = insert(:user, local: false, ap_id: "https://two.com/users/bob") - note = insert(:note) - - like = %{ - "type" => "Like", - "actor" => bob.ap_id, - "id" => "https://two.com/activities/actually-forged-like", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "object" => note.data["id"] - } - - assert_mismatched_signature_cancelled(like, alice) - refute Activity.get_by_ap_id(like["id"]) - end - - test "cancels signature actor mismatch before processing a forged Announce" do - alice = insert(:user, local: false, ap_id: "https://one.com/users/alice") - bob = insert(:user, local: false, ap_id: "https://two.com/users/bob") - note = insert(:note) - - announce = %{ - "type" => "Announce", - "actor" => bob.ap_id, - "id" => "https://two.com/activities/forged-announce", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "object" => note.data["id"] - } - - assert_mismatched_signature_cancelled(announce, alice) - end - - test "cancels signature actor mismatch before processing a forged Follow" do - alice = insert(:user, local: false, ap_id: "https://one.com/users/alice") - bob = insert(:user, local: false, ap_id: "https://two.com/users/bob") - followed = insert(:user) - - follow = %{ - "type" => "Follow", - "actor" => bob.ap_id, - "id" => "https://two.com/activities/forged-follow", - "to" => [followed.ap_id], - "cc" => [], - "object" => followed.ap_id - } - - assert_mismatched_signature_cancelled(follow, alice) - end - - test "cancels signature actor mismatch before processing a forged Undo" do - alice = insert(:user, local: false, ap_id: "https://one.com/users/alice") - bob = insert(:user, local: false, ap_id: "https://two.com/users/bob") - - undo = %{ - "type" => "Undo", - "actor" => bob.ap_id, - "id" => "https://two.com/activities/forged-undo", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "object" => "https://two.com/activities/existing-bob-activity" - } - - assert_mismatched_signature_cancelled(undo, alice) - end -end