Skip to content

REST API: Add the rest_allowed_cors_methods filter and stop discarding a response's method list. - #13151

Closed
moonmeister wants to merge 45 commits into
WordPress:trunkfrom
moonmeister:fix/rest-cors-allow-methods-clobber
Closed

moonmeister wants to merge 45 commits into
WordPress:trunkfrom
moonmeister:fix/rest-cors-allow-methods-clobber

Conversation

@moonmeister

@moonmeister moonmeister commented Aug 18, 2026 •

Copy link
Copy Markdown

Trac ticket: https://core.trac.wordpress.org/ticket/46992 - background, prior art and the caveat are there.

Approach discussed in person with @adamsilverstein, who closed the ticket in 2019 and has since agreed on option C.

Description refreshed by Claude to match the current head, 674e883.

Changes

1. Don't clobber a value set on the response. rest_send_cors_headers() runs on rest_pre_serve_request, after the response's own headers have been sent, and header() replaces by default - so the hardcoded list discarded anything set via rest_post_dispatch. A response value now seeds the method list instead, narrowed to the methods in the default list. Header names and method names are matched without regard to case.

$response_headers = ( $result instanceof WP_HTTP_Response ) ? $result->get_headers() : array();
$response_headers = array_change_key_case( $response_headers );

$allow_methods = array( 'OPTIONS', 'GET', 'POST', 'PUT', 'PATCH', 'DELETE' );
if ( isset( $response_headers['access-control-allow-methods'] ) ) {
	$allow_methods = array_values( array_intersect( $allow_methods, array_map( 'strtoupper', wp_parse_list( $response_headers['access-control-allow-methods'] ) ) ) );
}

get_headers() rather than headers_list(): it reads what the response intends rather than what PHP already emitted, and works under CLI.

2. Add rest_allowed_cors_methods. Same shape as the sibling CORS list filters rest_exposed_cors_headers and rest_allowed_cors_headers - array in, imploded out, $request in context. rest_pre_serve_request already passes $request, hence add_filter( ..., 10, 3 ).

$allow_methods = apply_filters( 'rest_allowed_cors_methods', $allow_methods, $request instanceof WP_REST_Request ? $request : null );

if ( ! empty( $allow_methods ) ) {
	header( 'Access-Control-Allow-Methods: ' . implode( ', ', $allow_methods ) );
} else {
	header_remove( 'Access-Control-Allow-Methods' );
}

The filter always runs, so a callback can add methods outside the default list, narrow the list, or replace a value the response set. An empty list omits the header, and removes one the response already sent.

Both are needed: (1) alone forces a plugin to restate the whole list, so two plugins conflict instead of composing; (2) alone still gets discarded when a response sets its own value.

$result and $request are untyped, so an unexpected value passed through rest_pre_serve_request is ignored rather than fatal. The default output is unchanged for every site.

Caveat: under ?_envelope=1, envelope_response() moves the response's headers into the body before this runs, so a response value is not seen and the filtered default is sent.

Tests

rest_send_cors_headers() had no coverage at all. New file, 11 tests: default list, no Origin, response value passed through and narrowed, case-insensitive header and method matching, unexpected arguments ignored, filter add and override, the request in context when serving, and the envelope caveat.

  • PHP does not record header() under the CLI SAPI, so the tests read the final list from a rest_allowed_cors_methods callback at PHP_INT_MAX instead of from the wire. No Xdebug needed, and nothing skips silently.
  • That also means the header_remove() branch for an empty list has no test. Xdebug doesn't track header_remove() either, so there is no way to observe it under CLI.
  • --group restapi: 3746 tests, no failures.

Proposed commit message

Getting this one ready to land, here is the message I plan to commit it with.

Claude drafted this from the PR and the ticket history:

REST API: Introduce the `rest_allowed_cors_methods` filter.

Add a filter to `rest_send_cors_headers()` so the `Access-Control-Allow-Methods` header sent with CORS responses can be adjusted. The list of methods is a fixed default and is not derived from the registered routes, so a site serving routes that accept other methods can now advertise them. The filter receives the method list as an array and the request in context, matching the shape of the sibling `rest_exposed_cors_headers` and `rest_allowed_cors_headers` filters, so a plugin can append to or narrow the list rather than restate it. An empty list omits the header.

The filter is seeded from an `Access-Control-Allow-Methods` header already set on the response, limited to the methods in the default list, falling back to the default list itself. A value set through `rest_post_dispatch` was previously discarded: `rest_send_cors_headers()` runs on `rest_pre_serve_request`, after the response's own headers have been emitted, and `header()` replaces by default.

The default output is unchanged for sites that neither set the header nor use the filter. Under `?_envelope=1` a response header is relocated into the response body before this runs, so it is not seen here and the filtered default is sent. This also adds the first test coverage for `rest_send_cors_headers()`.

Follow-up to [56096].

Props moonmeister, sudar, TimothyBlynJacobs, aaemnnosttv, westonruter.
Fixes #46992.

Shout if the props list is missing anyone, or if the framing is off.

AI Use

My follow-up commits on this branch and the refreshed description came out of a session with 🤖 Claude Code. I will review and test.

moonmeister and others added 3 commits August 17, 2026 16:02
`Access-Control-Allow-Methods` is the only CORS list header REST API responses
send that has no extension point. Its two siblings gained one in 5.5.0 --
`rest_exposed_cors_headers` and `rest_allowed_cors_headers`, both array-filtered
then imploded in `WP_REST_Server::send_headers()`, and both given the request in
context in 6.3.0. This one was missed each time, because it is sent from
`rest_send_cors_headers()` rather than from the server.

Adds a filter with the same shape, so a site can compose the list rather than
unhook `rest_send_cors_headers` and reimplement it. `$request` is available
because `rest_pre_serve_request` already passes it; the filter accepts three
arguments to reach it.

The filter runs only when the response has not set the header itself, which is
the preceding change on this branch. A value on the response is the more
specific instruction, and there is no default left to shape once it is present.

Adds three tests to the existing CORS header coverage.

See #46992.
Keeps the two facts the code does not show -- that this runs after the
response's own headers have been sent, and that `header()` replaces by default
-- and drops the sentence restating the conditional below it.

See #46992.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@moonmeister
moonmeister marked this pull request as ready for review August 18, 2026 23:03
@github-actions

github-actions Bot commented Aug 18, 2026 •

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs.

Core Committers: Use this line as a base for the props when committing in SVN:

Props moonmeister, adamsilverstein, westonruter, audrasjb, sudar.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.


Props Bot commands

You can trigger Props Bot actions by commenting on this PR:

  • @propsbot add <username> to give props to a GitHub user. Requires write access.
  • @propsbot remove <username> to leave a user out of the props. Requires write access.
  • @propsbot refresh to refresh this list. Adding the props-bot label works too.

@github-actions

Copy link
Copy Markdown

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@adamsilverstein

adamsilverstein commented Aug 31, 2026 •

Copy link
Copy Markdown
Member

Thanks for the continued effort here. Overall this looks good to me @moonmeister!

Claude went over the PR against the ticket discussion, here is most of what came back:

The filter can add methods but it cannot take any away. When the response has set Access-Control-Allow-Methods, rest_allowed_cors_methods never runs at all, which test_response_value_takes_precedence_over_the_filter pins down with assertFalse( $filter_ran ).

That rules out the hardening case. A site narrowing the list to OPTIONS, GET is silently overridden by any plugin that sets the header on its own response, with no way to observe it. It also diverges from the two sibling filters this one is modeled on: rest_exposed_cors_headers and rest_allowed_cors_headers always apply.

Comment 11 on the ticket did not settle precedence either way, so this looks open. Seeding the filter with the response value rather than skipping it keeps option C intact and gives the site the last word:

$allow_methods = isset( $response_headers['access-control-allow-methods'] )
	? array_map( 'trim', explode( ',', $response_headers['access-control-allow-methods'] ) )
	: array( 'OPTIONS', 'GET', 'POST', 'PUT', 'PATCH', 'DELETE' );

$allow_methods = apply_filters( 'rest_allowed_cors_methods', $allow_methods, $request );
header( 'Access-Control-Allow-Methods: ' . implode( ', ', $allow_methods ) );

The response still wins over the default, and the one test above inverts.

Smaller things:

  • @param WP_HTTP_Response $result should be WP_HTTP_Response|null since it defaults to null, matching the WP_REST_Request|null on the next line. Column alignment does not change, both are 21 characters.
  • Under ?_envelope=1 the guard sees an empty header list. envelope_response() runs after rest_post_dispatch and builds a fresh response with the old headers relocated into the body, so a plugin's value lands in the JSON and core's default goes on the wire. That is arguably what _envelope is for, so a docblock line seems like enough here rather than a code change.

What do you think - is there a reason to keep the response winning outright that I'm not seeing?

…d list.

The filter previously ran only when the response had not set
`Access-Control-Allow-Methods` itself, on the reasoning that a value on the
response is the more specific instruction. That reverses here, per review: the
filter now always runs, seeded with the response's value when there is one and
with the default list otherwise.

Seeding is a strict superset of the previous behavior. It can express all three
outcomes -- filter wins by returning its own list, response wins by returning
`$allow_methods` untouched or by registering nothing, and the two compose by
appending. Skipping the filter made "filter overrides a response value"
unreachable at any priority, and gave the callback no way to observe that it had
been bypassed. Default output is unchanged either way.

Parsing the header back into an array also normalizes the append case:
`WP_HTTP_Response::header()` comma-joins when called with `$replace` false, so
`explode()` and `trim()` recover the individual methods.

Also types `$result` as `WP_HTTP_Response|null`, matching its default, and notes
that `envelope_response()` relocates the response's headers into the body before
this runs, so under `?_envelope=1` a response value is not seen here.

Inverts the test that pinned the old precedence and adds two: one asserting the
filter receives the response's value rather than the default, one covering the
envelope case.

See #46992.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@moonmeister

moonmeister commented Sep 15, 2026 •

Copy link
Copy Markdown
Author

Thanks for the review @adamsilverstein!

No, there's no reason I can find to keep it — taking your suggestion as written. The filter now always runs, seeded with the response's value when available.

The argument I'd hang it on is that seeding is a strict superset of the current behavior. With it, you can express all three outcomes: filter wins (return your own list), response wins (return $methods untouched, or register nothing at all), and compose ($methods[] = 'X'). With "skip-when-set", the "filter overrides a response value" is unreachable at any priority, and the callback can't even observe that it was bypassed. Default output is identical either way, so it's a free gain in expressiveness. Secondary support: nothing in WP privileges a value already set on an object over a filter that runs later, and rest_allowed_cors_methods on rest_pre_serve_request is later than rest_post_dispatch — so the skip was the deviation, not the fix.

Small bonus from parsing the header back out: WP_HTTP_Response::header( $key, $value, false ) comma-joins on append, so explode + trim normalizes the multi-append case for free.

On _envelope: agreed, docblock only. It's narrower than it first looks, too — Access-Control-Allow-Methods is only ever read by the browser off a preflight response, so the enveloped case only bites on an OPTIONS preflight to a URL already carrying ?_envelope=1. And it's a pre-existing property of the envelope that relocates all headers, not something this change introduces. Documented it and added a test so the behavior is at least pinned as intentional.

$result is now WP_HTTP_Response|null. Both small ones done.

moonmeister and others added 3 commits September 15, 2026 15:52
…meister/wordpress-develop into fix/rest-cors-allow-methods-clobber

* 'fix/rest-cors-allow-methods-clobber' of github.com:moonmeister/wordpress-develop:
  Docs: Add missing descriptions and declare types for `Translation_Entry` properties.
  Docs: Remove a blank line between `@param` and `@return` in core docblocks.
  Build/Test Tools: Stop committing changes to tracked files in pull requests.
  Code Quality: Resolve return.empty PHPStan errors in baseline.
  Build/Test Tools: Make `SLACK_GHA_TIMEOUT_WEBHOOK` optional.
  Filesystem API: Reject UNC and device paths in `validate_file()`.
  Tests: Use `assertIsCallable()` for callable assertions.
  Build/Test Tools: Distinguish between cancelled and timed out workflow runs.
  Build/Test Tools: Remove all `npx` commands.
  Build/Test Tools: Allow text compression in the local environment.
@sudar

sudar commented Oct 1, 2026

Copy link
Copy Markdown

Really happy to see progress on a ticket that I opened in trac 7 years ago.

@adamsilverstein adamsilverstein left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work.

Comment thread tests/phpunit/tests/rest-api/rest-cors-headers.php Outdated
westonruter and others added 3 commits October 19, 2026 20:14
…nse value.

When a response set an Access-Control-Allow-Methods header naming no
valid method tokens, such as an empty string or 'GET;POST', the parsed
list was empty and the header was removed. Preflighted cross-origin PUT,
PATCH and DELETE requests then failed, where the default list had
always been sent before.

Use the default list in that case. Only an empty list from the
'rest_allowed_cors_methods' filter now omits the header.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… since tag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Without the D modifier, `$` in the method token pattern also matches
before a final newline, so an entry such as "PUT\n" passed validation.
Sent without a server, header() then rejects the whole
Access-Control-Allow-Methods value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@westonruter westonruter left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude found some more stuff at high effort. One moment.

westonruter and others added 9 commits October 19, 2026 20:37
wp_parse_list() keeps any scalar in an array, and preg_grep() matches the
stringified value but returns the original, so a filter returning
array( 'GET', true ) produced "GET, 1" and the parsed list held values
that were not strings. Keep only the string entries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A filter returning array( 'GET, POST' ) or array( ' GET' ) had every
entry fail the method token check, so the header was omitted, while the
same text returned as a string worked. Join the array entries before
parsing, so they are split on commas and whitespace the same way.

Line breaks are now separators too, so they cannot reach the header.
The test for invalid tokens is split accordingly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…server.

The 'rest_allowed_cors_methods' filter documents that an empty list
omits the header, but without a server an Access-Control-Allow-Methods
header that was already set stayed in place. Remove it with
header_remove() in that case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The class docblock still described an earlier approach, reading the
method list from a late filter callback. The tests now read the headers
that rest_send_cors_headers() sends through Spy_REST_Server.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DEFAULT_ALLOW_METHODS was defined but never used, while five assertions
repeated the default list as a literal. Make the constant the header
value and assert against it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
set_up() sets `$_SERVER['REQUEST_METHOD']` to 'GET', but tear_down() did
not restore it, so the value leaked into later tests and made their
results depend on test order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A 'rest_allowed_cors_methods' callback that forgets to return the list,
or returns false or null, made the parsed list empty and so removed the
Access-Control-Allow-Methods header, breaking preflighted cross-origin
PUT, PATCH and DELETE requests across the site.

Keep the unfiltered list when the filtered value is neither an array
nor a string. An empty array or string still omits the header.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A filter appending a method already in the list, such as GET, repeated
it in the Access-Control-Allow-Methods header. Remove duplicates when
parsing the list. Methods are case-sensitive, so entries differing only
in case are kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tear_down() unset `$_SERVER['HTTP_ORIGIN']` rather than restoring any
value it had before the test, unlike the request method. Save and
restore both the same way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@audrasjb audrasjb left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically approved on my side, let's ship it ahead of 7.2 beta 2.

@adamsilverstein

Copy link
Copy Markdown
Member

Thanks for the reviews, I'll get this committed before beta 2.

@github-actions

Copy link
Copy Markdown

A commit was made that fixes the Trac ticket referenced in the description of this pull request.

SVN changeset: 64730
GitHub commit: 9c9e35e

This PR will be closed, but please confirm the accuracy of this and reopen if there is more work to be done.

@github-actions github-actions Bot closed this Oct 26, 2026
@adamsilverstein

adamsilverstein commented Oct 26, 2026 •

Copy link
Copy Markdown
Member

Here is a first pass at the dev note for this one, aiming to fold it into the REST API or miscellaneous 7.2 dev notes post. Review and edits welcome, especially from @moonmeister and @sudar.

Claude drafted the dev note from the commit and the ticket history:

REST API: New rest_allowed_cors_methods filter in WordPress 7.2

WordPress 7.2 adds a rest_allowed_cors_methods filter for the Access-Control-Allow-Methods header that the REST API sends with cross-origin (CORS) requests. It also stops discarding an Access-Control-Allow-Methods value set on a REST response.

The problem

When a request carries an Origin header, rest_send_cors_headers() sends a hardcoded list of methods: OPTIONS, GET, POST, PUT, PATCH, DELETE. There was no way to change that list short of unhooking rest_send_cors_headers() from rest_pre_serve_request and re-implementing it, or hooking in at a later priority and calling header() yourself. Both are fragile, and two plugins doing it will conflict.

Setting the header on the response through rest_post_dispatch, the usual advice for changing REST response headers, did not work either. rest_send_cors_headers() runs after the response's own headers have been sent, and replaced whatever value the response set.

The two sibling CORS headers have had filters since 5.5: rest_exposed_cors_headers for Access-Control-Expose-Headers and rest_allowed_cors_headers for Access-Control-Allow-Headers. This filter fills in the third.

The new filter

rest_allowed_cors_methods follows the same shape as its siblings. It receives the list of methods as an array, along with the request in context, and the result is joined into the header.

/**
 * @param string[]             $allow_methods The list of HTTP methods to allow.
 * @param WP_REST_Request|null $request       The request in context, if available.
 */
apply_filters( 'rest_allowed_cors_methods', $allow_methods, $request );

Since it is an array, plugins can add to or narrow the list without restating it, and several plugins can do so without stepping on each other.

Advertise an extra method, eg. for a route that accepts PROPFIND:

add_filter(
	'rest_allowed_cors_methods',
	function ( array $methods ): array {
		$methods[] = 'PROPFIND';
		return $methods;
	}
);

Narrow the list for cross-origin requests to a read-only namespace:

add_filter(
	'rest_allowed_cors_methods',
	function ( array $methods, ?WP_REST_Request $request ): array {
		if ( $request && str_starts_with( $request->get_route(), '/my-plugin/v1/public' ) ) {
			return array( 'OPTIONS', 'GET' );
		}
		return $methods;
	},
	10,
	2
);

Returning an empty array omits the Access-Control-Allow-Methods header entirely. Entries that are not valid HTTP method tokens are dropped, and a callback that returns something other than an array or a string is ignored in favor of the unfiltered list.

Response values are no longer discarded

If a response sets its own Access-Control-Allow-Methods header, for example from rest_post_dispatch, that value is now used as the starting list passed to the filter instead of being overwritten by the default:

add_filter(
	'rest_post_dispatch',
	function ( WP_HTTP_Response $response ): WP_HTTP_Response {
		$response->header( 'Access-Control-Allow-Methods', 'OPTIONS, GET' );
		return $response;
	}
);

The filter still runs after that, so a site always has the last word: a callback can keep the response's value, extend it, or replace it. If the response value names no valid methods, the default list is used.

Backward compatibility

The default output is unchanged. A site that neither sets the header on a response nor uses the filter sends the same OPTIONS, GET, POST, PUT, PATCH, DELETE list as before.

rest_send_cors_headers() gains three optional parameters, $result, $request, and $server, and is now hooked to rest_pre_serve_request with four accepted arguments. Existing calls with a single argument keep working, and remove_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' ) still removes it, since the priority is unchanged at 10.

When serving a REST request, the CORS headers are now sent through WP_REST_Server::send_header() rather than a direct header() call.

Edge case: ?_envelope=1

With _envelope set, the response's headers are moved into the response body before rest_send_cors_headers() runs, so a value set on the response is not seen and the filtered default is sent. The filter itself still applies.

Further reading

Props to ... for technical review and ... for copy review.

@adamsilverstein

Copy link
Copy Markdown
Member

@moonmeister - if you have a moment I would appreciate your review of the dev-note above - #13151 (comment)

@sudar

sudar commented Oct 26, 2026

Copy link
Copy Markdown

@adamsilverstein Looks good to me.

Thanks for handling it.

@moonmeister

Copy link
Copy Markdown
Author

Generally looks good, Couple things:

Read-only example

Narrow the list for cross-origin requests to a read-only namespace:

add_filter(
	'rest_allowed_cors_methods',
	function ( array $methods, ?WP_REST_Request $request ): array {
		if ( $request && str_starts_with( $request->get_route(), '/my-plugin/v1/public' ) ) {
			return array( 'OPTIONS', 'GET' );
		}
		return $methods;
	},
	10,
	2
);

Access-Control-Allow-Methods only affects browser preflight requests. Simple cross-origin POSTs aren't preflighted, and non-browser clients ignore the header. A sentence saying "this is not a permission check; use permission_callback" might help stop people from relying on it.

Potential Type hint issues

Advertise an extra method, eg. for a route that accepts PROPFIND:

add_filter(
	'rest_allowed_cors_methods',
	function ( array $methods ): array {
		$methods[] = 'PROPFIND';
		return $methods;
	}
);
  1. Core accepts a string from a callback. If a callback returns 'OPTIONS, GET' instead of array( 'OPTIONS', 'GET' ), core parses it fine. The note implies this too.
  2. Filter callbacks run in a chain. Each callback receives the previous one's return value.

So picture two plugins:

// Plugin A: returns a string. Core is fine with this.
add_filter( 'rest_allowed_cors_methods', fn() => 'OPTIONS, GET' );
// Plugin B: copied from the dev note's example.
add_filter( 'rest_allowed_cors_methods', function ( array $methods ): array {
        $methods[] = 'PROPFIND';
        return $methods;
}, 20 );

Plugin B receives Plugin A's string. Its array $methods type hint makes PHP throw a TypeError, and the request fails. Neither plugin is doing anything the note says is wrong, but together they crash.

The fix is to the note: we should probably drop the array type hints from the examples (function ( $methods )). and instead . We can follow the example of rest_exposed_cors_headers and rest_allowed_cors_headers, whose docs promise string[] and make sure to encourage that. Either way, the examples shouldn't encourage a type hint that core's own behavior can break.

We can update the example with some defensive code too:

add_filter(
        'rest_allowed_cors_methods',
        function ( $methods ) {
                $methods   = wp_parse_list( $methods );
                $methods[] = 'PROPFIND';
                return $methods;
        }
);

@adamsilverstein

Copy link
Copy Markdown
Member

The final version of this dev note is in the REST API dev note comments on #14359: #14359 (comment)

@adamsilverstein

Copy link
Copy Markdown
Member

Thanks for the feedback @moonmeister - I'll make sure it is applied.

@adamsilverstein

Copy link
Copy Markdown
Member

Thanks @moonmeister, both points are applied in the final version of the dev note: #14359 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants