Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Presence of <SVG in POSTed content results in 403 #272

Open
tonzyl opened this issue Jan 19, 2022 · 7 comments
Open

Presence of <SVG in POSTed content results in 403 #272

tonzyl opened this issue Jan 19, 2022 · 7 comments

Comments

@tonzyl
Copy link

tonzyl commented Jan 19, 2022

I've created a basic PHP Micropub client to talk to my WordPress Micropub endpoint. It uses form-urlencoded and calls the endpoint with file_get_contents()
This works all as intended.

When I include an SVG icon in the html content however the endpoint returns a 403 error. It was pointed out to me that this might be an issue with WP core, not with the plugin ( https://core.trac.wordpress.org/ticket/30377 https://core.trac.wordpress.org/ticket/54244 ). However that seems to be about GET requests and urls, not POST requests.

Original HTML that results in 403:
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" width="19" height="19"><path d="M576 240c0 115-129 208-288 208-48.3 0-93.9-8.6-133.9-23.8-40.3 31.2-89.8 50.3-142.4 55.7-5.2.6-10.2-2.8-11.5-7.7-1.3-5 2.7-8.1 6.6-11.8 19.3-18.4 42.7-32.8 51.9-94.6C21.9 330.9 0 287.3 0 240 0 125.1 129 32 288 32s288 93.1 288 208z"/></svg> <em>Some text here</em>'

but pairing it down showed that

'some text <svg></svg> more text'

also gets a 403 and even

'some text <svg some other text'

gets a 403.
If I leave out the first < in the original html it also posts fine, it really is down to the presence of '<svg'

@tonzyl
Copy link
Author

tonzyl commented Jan 19, 2022

As there were some oddities w.r.t. reproducing this effect by others, here the full code of the script I'm using to test. There are four pieces of content to test POSTing.
Only $contentspul4 results in a post, the others don't unless you remove the < before svg.

<?php
$bearertoken ="secrettoken";

$formurl = "https://zylstra.org/blog/wp-json/micropub/1.0/endpoint";

$contentspul ='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" width="19" height="19"><path d="M576 240c0 115-129 208-288 208-48.3 0-93.9-8.6-133.9-23.8-40.3 31.2-89.8 50.3-142.4 55.7-5.2.6-10.2-2.8-11.5-7.7-1.3-5 2.7-8.1 6.6-11.8 19.3-18.4 42.7-32.8 51.9-94.6C21.9 330.9 0 287.3 0 240 0 125.1 129 32 288 32s288 93.1 288 208z"/></svg> <em>In reply to <a href="https://www.zylstra.org/blog/2022/01/php-https-post-issue-solved/" class="p-name u-in-reply-to">PHP HTTPS Post Issue Solved</a> by Ton Zijlstra</em>'."\n\nDit gaat nu wel goed toch?<br/>\n\n<blockquote>However the content of the post is missing, so now the next step is ensuring I am posting to my site in the right format.<br/><br/>Ton Zijlstra</blockquote>";
$contentspul2= 'text <svg></svg> more text'."\n\nDit gaat nu wel goed toch?<br/>\n\n<blockquote>However the content of the post is missing, so now the next step is ensuring I am posting to my site in the right format.<br/><br/>Ton Zijlstra</blockquote>";
$contentspul3 = 'BOE <svg BOE';
$contentspul4 = 'some text without svg';

/* below change the name of the variable after html to use one or the other for testing 
*/

$formproperties = array (
	'type' => "h-entry",
	'name' => "Dit is test titel 4",
	'content' => array (
	'html' => $contentspul
	),
	'category[0]' => "IndieWeb",
	'category[1]' => "agency",
	'post-status' => "draft"
	);
	
$headers = [

'Accept: application/x-www-form-urlencoded',

'Content-type: application/x-www-form-urlencoded',

'Authorization: Bearer '.$bearertoken,

];

$formOptions = array(
    'http' => array(
        'header'  => $headers,
        'method'  => 'POST',
        'content' => http_build_query($formproperties)
    )
);

$context = stream_context_create($formOptions);
$resp = file_get_contents($formurl, false, $context);

?>

@Ruxton
Copy link
Contributor

Ruxton commented Jan 19, 2022

@tonzyl don't quote me on this but on first glance (i haven't tried the code yet), you're using the structure of the JSON post style but doing a form-urlencoded post, I'm not sure this is supported? but also:

Nested objects require that the request be sent in JSON format, since the form-encoding syntax does not consistently support nested objects.

So at the very least your Content-Type and Accept should be application/json and build up a json object with that structure. Or.. change your fields so they're in the accepted structure for urlencoded posts https://www.w3.org/TR/micropub/#form-encoded-and-multipart-requests

@tonzyl
Copy link
Author

tonzyl commented Jan 19, 2022

Hi @Ruxton that only pertains to the categories in this case, right? When I tried to nest those that didn't work, but adding them as separate fields does work in this case. Without them the issue is the same though. (And without the <svg bit, the entire construct works. E.g. this https://www.zylstra.org/blog/2022/01/my-first-micropub-posting/ was posted with the script above.) I first ensured the calls work, then that the structure of the POSTed info worked, and then started playing with the actual posting content submitted which surfaced the issue. (And if you mean the content/html nesting, POSTing only works if done that way)

If I switch to JSON (though I prefer form, which is also the default in the spec iirc), simply changing the headers is not enough I assume (I think I tried that earlier but then the call fails).

Warning: limited coding skills here, so going by trial and error.

@janboddez
Copy link
Contributor

janboddez commented Jan 19, 2022

[T]hat only pertains to the categories in this case, right?

Well, content is an array, too. You might wanna try just making that an HTML string and see if your server accepts it or strips away the HTML.

Also those categories look a bit ... off? Normally you'd define them like so:

$props = array (
    ...
    'content' => $contentspul, // As explained above.
    ...
    'category' => array( 'IndieWeb', 'agency' ),
    'post-status' => 'draft',
    ...
);

Note that, IIRC, http_build_query() will add the numerical keys anyhow. Like, there's no difference between http_build_query( array( 'IndieWeb', 'agency' ) ) and http_build_query( array( 0 => 'IndieWeb', 1 => 'agency' ) ). Both would ouput category[0]=IndieWeb&category[1]=agency.

(Now, because some APIs don't like these keys and instead expect category[]=IndieWeb&category[]=agency, you might wanna eventually find a workaround that, too. That said, if the above works, it works. Still mentioning it because the examples at https://www.w3.org/TR/micropub/#new-note use this syntax, too. Whether it works depends on the server.)

@janboddez
Copy link
Contributor

janboddez commented Jan 19, 2022

To encode the request body as JSON, you could probably:

$props = [
    'type' => 'h-entry',
    'name' => 'Dit is test titel 4',
    'content' => ['html' => $contentspul],
    'category' => ['IndieWeb', 'agency'],
    'post-status' => 'draft',
];

$options = [
    'http' => [
        'header' => [
            'Content-type: application/json',
            'Authorization: Bearer '.$bearertoken,
        ],
        'method' => 'POST',
        'content' => json_encode($props),
    ],
    ...
];

Or something. That would turn content (the one inside $props) into an object with a html property. The JSON would look something like:

{"type":"h-entry","name":"Dit is test titel 4","content":{"html":"huppeldepup"},"category":["IndieWeb","agency"],"post-status":"draft"}

@tonzyl
Copy link
Author

tonzyl commented Jan 19, 2022

@janboddez , thanks for responding (here and in other channels).
If I just do the HTML string, sending to the endpoint fails locally at file_get_contents not opening the stream.
If I do categories as an array the way you, and the Micropub spec describe it, only the last one gets used server-side, if I define them separately they all get processed correctly.

Micropub is spec'd such that it must work with form-urlencoded, and JSON is optional, so I am somewhat surprised by treating JSON as the default. https://www.w3.org/TR/micropub/#create

Anyway, the oddity is in how content gets filtered on <svg, resulting in a 403 error. The rest works as intended and results in correct postings on the blog.
I may try posting in JSON tomorrow to see if the same thing happens w.r.t. <svg or not.

@tonzyl
Copy link
Author

tonzyl commented Jan 20, 2022

Update: While I haven't figured out what causes the issue I started with above, I have made progress.

I've converted my request to the Micropub endpoint into JSON, while still submitting it with the php file_get_contents function. Then the SVG gets accepted! I'd prefer using form-urlencode as it is the Micropub standard's default, but if this works it works.

Could not get the JSON to work with curl_exec though, but that's not for here, and something to further explore on my own.

To get the Micropub endpoint, using file_get_contents, to accept the JSON correctly I did have to change the way I constructed the submitted info a bit.

This is what I ended up with:

$postitle ='some catchy title';
$contenspul = 'some html content';
$html = ['html' => $contentspul]; /* create an array out of the html content */

$formproperties = array (
	'properties' => array ( /*this properties array wrapper is not needed when using http-encode*/
	'type' => 'h-entry',
	'name' => $posttitel,
	'content' => [$html], /* put the html arry between brackets. defining content here as html=>contentspul results in malformed micropub request and no content in the posting, while title and categories go ok */
	'category' => ['test','timeline', 'agency'],
	'post-status' => 'draft'
	)
	);
	
$jsondata = json_encode($formproperties);

$headers = [
'Accept: application/json',
'Content-type: application/json',
'Authorization: Bearer '.$bearertoken
];

$formOptions = array(
    'http' => array(
        'header'  => $headers,
        'method'  => 'POST',
        'content' => $jsondata
    )
);

$context = stream_context_create($formOptions);
$resp = file_get_contents($formurl, false, $context);

@janboddez using JSON providing the categories works normally now. But I did have to package the posting's variables a bit differently in arrays to get to the right result at the micropub endpoint.

Why SVG doesn't work with form-url-encode remains a mystery to me.

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

No branches or pull requests

3 participants