Dotclear

source: plugins/importExport/inc/class.dc.import.feed.php @ 3269:da4849c1ad84

Revision 3269:da4849c1ad84, 5.4 KB checked in by franck <carnet.franck.paul@…>, 9 years ago (diff)

Prevents SSRF/XSPA on Feed import, thanks wiswat for reporting this.

Line 
1<?php
2# -- BEGIN LICENSE BLOCK ---------------------------------------
3#
4# This file is part of importExport, a plugin for DotClear2.
5#
6# Copyright (c) 2003-2012 Olivier Meunier & Association Dotclear
7# Licensed under the GPL version 2.0 license.
8# See LICENSE file or
9# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
10#
11# -- END LICENSE BLOCK -----------------------------------------
12if (!defined('DC_RC_PATH')) { return; }
13
14class dcImportFeed extends dcIeModule
15{
16     protected $status = false;
17     protected $feed_url = '';
18
19     // IPv6 functions (from https://gist.github.com/tbaschak/7866688)
20     private function gethostbyname6($host,$try_a = false) {
21        // get AAAA record for $host
22        // if $try_a is true, if AAAA fails, it tries for A
23        // the first match found is returned
24        // otherwise returns false
25
26        $dns = $this->gethostbynamel6($host,$try_a);
27        if ($dns == false) {
28          return false;
29        }
30        else {
31          return $dns[0];
32        }
33    }
34    private function gethostbynamel6($host,$try_a = false) {
35        // get AAAA records for $host,
36        // if $try_a is true, if AAAA fails, it tries for A
37        // results are returned in an array of ips found matching type
38        // otherwise returns false
39
40        $dns6 = dns_get_record($host,DNS_AAAA);
41        if ($try_a == true) {
42            $dns4 = dns_get_record($host,DNS_A);
43            $dns = array_merge($dns4,$dns6);
44        }
45        else {
46          $dns = $dns6;
47        }
48        $ip6 = array();
49        $ip4 = array();
50        foreach ($dns as $record) {
51            if ($record["type"] == "A") {
52                $ip4[] = $record["ip"];
53            }
54            if ($record["type"] == "AAAA") {
55                $ip6[] = $record["ipv6"];
56            }
57        }
58        if (count($ip6) < 1) {
59            if ($try_a == true) {
60                if (count($ip4) < 1) {
61                    return false;
62                }
63                else {
64                    return $ip4;
65                }
66            }
67            else {
68                return false;
69            }
70        }
71        else {
72            return $ip6;
73        }
74    }
75
76     public function setInfo()
77     {
78          $this->type = 'import';
79          $this->name = __('RSS or Atom feed import');
80          $this->description = __('Add a feed content to the blog.');
81     }
82
83     public function process($do)
84     {
85          if ($do == 'ok') {
86               $this->status = true;
87               return;
88          }
89
90          if (empty($_POST['feed_url'])) {
91               return;
92          }
93
94          $this->feed_url = $_POST['feed_url'];
95
96          // Check feed URL
97          if ($this->core->blog->settings->system->import_feed_url_control) {
98               // Get IP from URL
99               $bits = parse_url($this->feed_url);
100               if (!$bits || !isset($bits['host'])) {
101                    throw new Exception(__('Cannot retrieve feed URL.'));
102               }
103               $ip = gethostbyname($bits['host']);
104               if ($ip == $bits['host']) {
105                    $ip = $this->gethostbyname6($bits['host']);
106                    if (!$ip) {
107                         throw new Exception(__('Cannot retrieve feed URL.'));
108                    }
109               }
110               // Check feed IP
111               $flag = FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6;
112               if ($this->core->blog->settings->system->import_feed_no_private_ip) {
113                    $flag |= FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE;
114               }
115               if (!filter_var($ip,$flag)) {
116                    throw new Exception(__('Cannot retrieve feed URL.'));
117               }
118               // IP control (white list regexp)
119               if ($this->core->blog->settings->system->import_feed_ip_regexp != '') {
120                    if (!preg_match($this->core->blog->settings->system->import_feed_ip_regexp,$ip)) {
121                         throw new Exception(__('Cannot retrieve feed URL.'));
122                    }
123               }
124               // Port control (white list regexp)
125               if ($this->core->blog->settings->system->import_feed_port_regexp != '' && isset($bits['port'])) {
126                    if (!preg_match($this->core->blog->settings->system->import_feed_port_regexp,$bits['port'])) {
127                         throw new Exception(__('Cannot retrieve feed URL.'));
128                    }
129               }
130          }
131
132          $feed = feedReader::quickParse($this->feed_url);
133          if ($feed === false) {
134               throw new Exception(__('Cannot retrieve feed URL.'));
135          }
136          if (count($feed->items) == 0) {
137               throw new Exception(__('No items in feed.'));
138          }
139
140          $cur = $this->core->con->openCursor($this->core->prefix.'post');
141          $this->core->con->begin();
142          foreach ($feed->items as $item)
143          {
144               $cur->clean();
145               $cur->user_id = $this->core->auth->userID();
146               $cur->post_content = $item->content ? $item->content : $item->description;
147               $cur->post_title = $item->title ? $item->title : text::cutString(html::clean($cur->post_content),60);
148               $cur->post_format = 'xhtml';
149               $cur->post_status = -2;
150               $cur->post_dt = strftime('%Y-%m-%d %H:%M:%S',$item->TS);
151
152               try {
153                    $post_id = $this->core->blog->addPost($cur);
154               } catch (Exception $e) {
155                    $this->core->con->rollback();
156                    throw $e;
157               }
158
159               foreach ($item->subject as $subject) {
160                    $this->core->meta->setPostMeta($post_id,'tag',dcMeta::sanitizeMetaID($subject));
161               }
162          }
163
164          $this->core->con->commit();
165          http::redirect($this->getURL().'&do=ok');
166
167     }
168
169     public function gui()
170     {
171          if ($this->status) {
172               dcPage::success(__('Content successfully imported.'));
173          }
174
175          echo
176          '<form action="'.$this->getURL(true).'" method="post">'.
177          '<p>'.sprintf(__('Add a feed content to the current blog: <strong>%s</strong>.'),html::escapeHTML($this->core->blog->name)).'</p>'.
178
179          '<p><label for="feed_url">'.__('Feed URL:').'</label>'.
180          form::field('feed_url',50,300,html::escapeHTML($this->feed_url)).'</p>'.
181
182          '<p>'.
183          $this->core->formNonce().
184          form::hidden(array('do'),1).
185          '<input type="submit" value="'.__('Import').'" /></p>'.
186
187          '</form>';
188     }
189}
Note: See TracBrowser for help on using the repository browser.

Sites map