Browse Source
This introduces a data model function of unusual privilege: it can retrieve favicon URLs for any subscription, regardless of user ID. This is a single-purpose hack and its use should be avoided if at all possible.microsub
J. King
7 years ago
6 changed files with 120 additions and 7 deletions
@ -0,0 +1,32 @@ |
|||
<?php |
|||
declare(strict_types=1); |
|||
namespace JKingWeb\Arsse\REST\TinyTinyRSS; |
|||
|
|||
use JKingWeb\Arsse\Arsse; |
|||
use JKingWeb\Arsse\REST\Response; |
|||
|
|||
class Icon extends \JKingWeb\Arsse\REST\AbstractHandler { |
|||
|
|||
|
|||
public function __construct() { |
|||
} |
|||
|
|||
public function dispatch(\JKingWeb\Arsse\REST\Request $req): Response { |
|||
if ($req->method != "GET") { |
|||
// only GET requests are allowed |
|||
return new Response(405, "", "", ["Allow: GET"]); |
|||
} elseif (!preg_match("<^(\d+)\.ico$>", $req->url, $match) || !((int) $match[1])) { |
|||
return new Response(404); |
|||
} |
|||
$url = Arsse::$db->subscriptionFavicon((int) $match[1]); |
|||
if ($url) { |
|||
// strip out anything after literal line-end characters; this is to mitigate a potential header (e.g. cookie) injection from the URL |
|||
if (($pos = strpos($url, "\r")) !== FALSE || ($pos = strpos($url, "\n")) !== FALSE) { |
|||
$url = substr($url, 0, $pos); |
|||
} |
|||
return new Response(301, "", "", ["Location: $url"]); |
|||
} else { |
|||
return new Response(404); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,54 @@ |
|||
<?php |
|||
declare(strict_types=1); |
|||
namespace JKingWeb\Arsse; |
|||
|
|||
use JKingWeb\Arsse\REST\Request; |
|||
use JKingWeb\Arsse\REST\Response; |
|||
use JKingWeb\Arsse\Test\Result; |
|||
use JKingWeb\Arsse\Misc\Date; |
|||
use JKingWeb\Arsse\Misc\Context; |
|||
use JKingWeb\Arsse\Db\ExceptionInput; |
|||
use JKingWeb\Arsse\Db\Transaction; |
|||
use JKingWeb\Arsse\REST\TinyTinyRSS\API; |
|||
use Phake; |
|||
|
|||
/** @covers \JKingWeb\Arsse\REST\TinyTinyRSS\Icon<extended> */ |
|||
class TestTinyTinyIcon extends Test\AbstractTest { |
|||
protected $h; |
|||
|
|||
public function setUp() { |
|||
$this->clearData(); |
|||
Arsse::$conf = new Conf(); |
|||
// create a mock user manager |
|||
// create a mock database interface |
|||
Arsse::$db = Phake::mock(Database::class); |
|||
$this->h = new REST\TinyTinyRSS\Icon(); |
|||
} |
|||
|
|||
public function tearDown() { |
|||
$this->clearData(); |
|||
} |
|||
|
|||
public function testRetrieveFavion() { |
|||
Phake::when(Arsse::$db)->subscriptionFavicon->thenReturn(""); |
|||
Phake::when(Arsse::$db)->subscriptionFavicon(42)->thenReturn("http://example.com/favicon.ico"); |
|||
Phake::when(Arsse::$db)->subscriptionFavicon(2112)->thenReturn("http://example.net/logo.png"); |
|||
Phake::when(Arsse::$db)->subscriptionFavicon(1337)->thenReturn("http://example.org/icon.gif\r\nLocation: http://bad.example.com/"); |
|||
// these requests should succeed |
|||
$exp = new Response(301, "", "", ["Location: http://example.com/favicon.ico"]); |
|||
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "42.ico"))); |
|||
$exp = new Response(301, "", "", ["Location: http://example.net/logo.png"]); |
|||
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "2112.ico"))); |
|||
$exp = new Response(301, "", "", ["Location: http://example.org/icon.gif"]); |
|||
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "1337.ico"))); |
|||
// these requests should fail |
|||
$exp = new Response(404); |
|||
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "ook.ico"))); |
|||
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "ook"))); |
|||
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "47.ico"))); |
|||
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "2112.png"))); |
|||
// only GET is allowed |
|||
$exp = new Response(405, "", "", ["Allow: GET"]); |
|||
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "2112.ico"))); |
|||
} |
|||
} |
Loading…
Reference in new issue