1<?php
2
3namespace dokuwiki\plugin\filelist\test;
4
5use dokuwiki\plugin\filelist\Path;
6use DokuWikiTest;
7
8/**
9 * Path related tests for the filelist plugin
10 *
11 * @group plugin_filelist
12 * @group plugins
13 */
14class PathTest extends DokuWikiTest
15{
16
17    protected $path;
18
19    public function setUp(): void
20    {
21        parent::setUp();
22
23        $this->path = new Path(
24            <<<EOT
25C:\\xampp\\htdocs\\wiki\\
26\\\\server\\share\\path\\
27/linux/file/path/
28/linux/another/path/../..//another/blargh/../path
29  A> alias
30  W> webfoo
31EOT
32        );
33    }
34
35    /**
36     * Test the configuration parsing for paths and aliases
37     */
38    public function testGetPaths()
39    {
40        $expect = [
41            'C:/xampp/htdocs/wiki/' => [
42                'root' => 'C:/xampp/htdocs/wiki/',
43                'web' => '/lib/plugins/filelist/file.php?root=C%3A%2Fxampp%2Fhtdocs%2Fwiki%2F&file=',
44            ],
45            '\\\\server/share/path/' => [
46                'root' => '\\\\server/share/path/',
47                'web' => '/lib/plugins/filelist/file.php?root=%5C%5Cserver%2Fshare%2Fpath%2F&file=',
48            ],
49            '/linux/file/path/' => [
50                'root' => '/linux/file/path/',
51                'web' => '/lib/plugins/filelist/file.php?root=%2Flinux%2Ffile%2Fpath%2F&file=',
52            ],
53            '/linux/another/path/' => [
54                'root' => '/linux/another/path/',
55                'alias' => 'alias/',
56                'web' => 'webfoo',
57            ],
58            'alias/' => [
59                'root' => '/linux/another/path/',
60                'alias' => 'alias/',
61                'web' => 'webfoo',
62            ],
63        ];
64
65        $this->assertEquals($expect, $this->path->getPaths());
66    }
67
68    /**
69     * Data provider for testGetPathInfoSuccess
70     */
71    public function providePathInfoSuccess()
72    {
73        return [
74            ['/linux/another/path', '/linux/another/path/'],
75            ['/linux/another/path/foo', '/linux/another/path/foo/'],
76            ['alias', '/linux/another/path/'],
77            ['alias/foo', '/linux/another/path/foo/'],
78            ['C:\\xampp\\htdocs\\wiki', 'C:/xampp/htdocs/wiki/'],
79            ['C:\\xampp\\htdocs\\wiki\\foo', 'C:/xampp/htdocs/wiki/foo/'],
80            ['\\\\server\\share\\path\\', '\\\\server/share/path/'],
81            ['\\\\server\\share\\path\\foo', '\\\\server/share/path/foo/'],
82        ];
83    }
84
85    /**
86     * @dataProvider providePathInfoSuccess
87     */
88    public function testGetPathInfoSuccess($path, $expect)
89    {
90        $pathInfo = $this->path->getPathInfo($path);
91        $this->assertEquals($expect, $pathInfo['path']);
92    }
93
94    public function providePathInfoFailure()
95    {
96        return [
97            ['/linux/file/path/../../../etc/'],
98            ['W:\\xampp\\htdocs\\wiki\\foo\\bar'],
99            ['/'],
100            ['./'],
101            ['../'],
102        ];
103    }
104
105    /**
106     * @dataProvider providePathInfoFailure
107     */
108    public function testGetPathInfoFailure($path)
109    {
110        $this->expectExceptionMessageMatches('/Path not allowed/');
111        $this->path->getPathInfo($path);
112    }
113
114    /**
115     * Relative paths have to be resolved to an absolute path so that file access works
116     * regardless of the current working directory (which differs between doku.php and file.php)
117     */
118    public function testRelativePathIsResolvedToAbsolute()
119    {
120        $path = new Path('relative/root/');
121        $pathInfo = $path->getPathInfo('relative/root/sub/file.txt', false);
122
123        $this->assertEquals(
124            Path::cleanPath(DOKU_INC, false) . '/relative/root/sub/file.txt',
125            $pathInfo['path']
126        );
127    }
128
129    /**
130     * Absolute configured paths must be passed through unchanged
131     */
132    public function testAbsolutePathIsKept()
133    {
134        $path = new Path('/somewhere/else/');
135        $pathInfo = $path->getPathInfo('/somewhere/else/file.txt', false);
136        $this->assertEquals('/somewhere/else/file.txt', $pathInfo['path']);
137    }
138
139    /**
140     * The wiki/data directory guard must trigger even for relatively configured roots.
141     *
142     * This is the regression behind issue #50: a relative root like "firmware" never matched
143     * the absolute DOKU_INC, so the guard was silently bypassed and wiki files could be served.
144     */
145    public function testIsWikiControlled()
146    {
147        global $conf;
148
149        // relative path inside the DokuWiki directory (cwd-independent)
150        $this->assertTrue(Path::isWikiControlled('lib/plugins/filelist'));
151        // absolute path inside the DokuWiki directory (e.g. the password hashes)
152        $this->assertTrue(Path::isWikiControlled(DOKU_INC . 'conf/users.auth.php'));
153        // the configured data directory
154        $this->assertTrue(Path::isWikiControlled($conf['savedir'] . '/pages/wiki/dokuwiki.txt'));
155        // a path completely outside the wiki must be allowed
156        $this->assertFalse(Path::isWikiControlled('/some/other/place/file.txt'));
157    }
158}
159