Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit fc779c5

Browse files
pfefferleobenland
andauthoredMay 13, 2025··
Send correct status header for Outbox items (#1685)
Co-authored-by: Konstantin Obenland <[email protected]>
1 parent 96e6437 commit fc779c5

File tree

3 files changed

+57
-7
lines changed

3 files changed

+57
-7
lines changed
 
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: patch
2+
Type: changed
3+
4+
Better error handling when other servers request Outbox items in the wrong format, and 404 pages now show correctly.

‎includes/class-activitypub.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ public static function render_activitypub_template( $template ) {
109109
self::add_headers();
110110

111111
if ( ! is_activitypub_request() || ! should_negotiate_content() ) {
112+
if ( \get_query_var( 'p' ) && Outbox::POST_TYPE === \get_post_type( \get_query_var( 'p' ) ) ) {
113+
\set_query_var( 'is_404', true );
114+
\status_header( 406 );
115+
}
112116
return $template;
113117
}
114118

@@ -139,7 +143,7 @@ public static function render_activitypub_template( $template ) {
139143
if ( $activitypub_template && use_authorized_fetch() ) {
140144
$verification = Signature::verify_http_signature( $_SERVER );
141145
if ( \is_wp_error( $verification ) ) {
142-
header( 'HTTP/1.1 401 Unauthorized' );
146+
\status_header( 401 );
143147

144148
// Fallback as template_loader can't return http headers.
145149
return $template;

‎tests/includes/class-test-activitypub.php

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
namespace Activitypub\Tests;
99

10+
use Activitypub\Activitypub;
11+
use Activitypub\Query;
1012
use Activitypub\Collection\Outbox;
1113

1214
/**
@@ -40,7 +42,7 @@ public static function wpSetUpBeforeClass( $factory ) {
4042
*/
4143
public function setUp(): void {
4244
parent::setUp();
43-
\Activitypub\Activitypub::init();
45+
Activitypub::init();
4446
}
4547

4648
/**
@@ -83,7 +85,7 @@ function () {
8385
);
8486

8587
// Test that the filter is applied.
86-
$template = \Activitypub\Activitypub::render_activitypub_template( 'original.php' );
88+
$template = Activitypub::render_activitypub_template( 'original.php' );
8789
$this->assertEquals( '/custom/template.php', $template, 'Custom preview template should be used when filter is applied.' );
8890

8991
// Clean up.
@@ -168,7 +170,7 @@ public function test_custom_post_type_returns_200() {
168170
$this->go_to( '/?p=' . $post_id );
169171

170172
// Test the template response.
171-
$template = \Activitypub\Activitypub::render_activitypub_template( 'index.php' );
173+
$template = Activitypub::render_activitypub_template( 'index.php' );
172174
$this->assertStringContainsString( 'activitypub-json.php', $template );
173175
$this->assertFalse( $wp_query->is_404 );
174176

@@ -211,7 +213,7 @@ public function test_custom_post_type_with_support_returns_200() {
211213
$this->go_to( '/?p=' . $post_id );
212214

213215
// Test the template response.
214-
$template = \Activitypub\Activitypub::render_activitypub_template( 'index.php' );
216+
$template = Activitypub::render_activitypub_template( 'index.php' );
215217
$this->assertStringContainsString( 'activitypub-json.php', $template );
216218
$this->assertFalse( $wp_query->is_404 );
217219

@@ -220,6 +222,46 @@ public function test_custom_post_type_with_support_returns_200() {
220222
_unregister_post_type( 'test_cpt_supported' );
221223
}
222224

225+
/**
226+
* Test 406/404 response for non-ActivityPub requests to Outbox post type.
227+
*
228+
* @covers ::render_activitypub_template
229+
*/
230+
public function test_outbox_post_type_non_activitypub_request_returns_406() {
231+
$data = array(
232+
'@context' => 'https://www.w3.org/ns/activitystreams',
233+
'id' => 'https://example.com/' . self::$user_id,
234+
'type' => 'Note',
235+
'content' => '<p>This is a note</p>',
236+
);
237+
$post_id = \Activitypub\add_to_outbox( $data, 'Create', self::$user_id );
238+
239+
$_SERVER['HTTP_ACCEPT'] = 'application/activity+json';
240+
$this->go_to( '/?p=' . $post_id );
241+
$template = Activitypub::render_activitypub_template( 'index.php' );
242+
$this->assertStringContainsString( 'activitypub-json.php', $template );
243+
244+
Query::get_instance()->__destruct();
245+
246+
$status = null;
247+
add_filter(
248+
'status_header',
249+
function ( $status_header ) use ( &$status ) {
250+
$status = $status_header;
251+
return $status_header;
252+
},
253+
100
254+
);
255+
256+
unset( $_SERVER['HTTP_ACCEPT'] );
257+
$this->go_to( '/?p=' . $post_id );
258+
$template = Activitypub::render_activitypub_template( 'index.php' );
259+
$this->assertStringContainsString( 'index.php', $template );
260+
$this->assertStringContainsString( '406', $status );
261+
262+
wp_delete_post( $post_id, true );
263+
}
264+
223265
/**
224266
* Test no_trailing_redirect method.
225267
*
@@ -231,15 +273,15 @@ public function test_no_trailing_redirect() {
231273
$requested_url = 'https://example.org/@testuser';
232274
$redirect_url = 'https://example.org/@testuser/';
233275

234-
$result = \Activitypub\Activitypub::no_trailing_redirect( $redirect_url, $requested_url );
276+
$result = Activitypub::no_trailing_redirect( $redirect_url, $requested_url );
235277
$this->assertEquals( $requested_url, $result, 'Should return requested URL when actor query var is set.' );
236278

237279
// Test case 2: When actor query var is not set, it should return the redirect URL.
238280
set_query_var( 'actor', '' );
239281
$requested_url = 'https://example.org/some-page';
240282
$redirect_url = 'https://example.org/some-page/';
241283

242-
$result = \Activitypub\Activitypub::no_trailing_redirect( $redirect_url, $requested_url );
284+
$result = Activitypub::no_trailing_redirect( $redirect_url, $requested_url );
243285
$this->assertEquals( $redirect_url, $result, 'Should return redirect URL when actor query var is not set.' );
244286

245287
// Clean up.

0 commit comments

Comments
 (0)
Please sign in to comment.