Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
D
drupal
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Custom Issue Tracker
Custom Issue Tracker
Labels
Merge Requests
225
Merge Requests
225
Requirements
Requirements
List
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Analytics
Analytics
Code Review
Insights
Issue
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
project
drupal
Commits
6323f118
Commit
6323f118
authored
Oct 15, 2012
by
webchick
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Issue
#1344078
by sun, chx, grendzy, webchick: Added Local image input filter in core.
parent
885f59b2
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
223 additions
and
0 deletions
+223
-0
core/CHANGELOG.txt
core/CHANGELOG.txt
+1
-0
core/modules/filter/filter.module
core/modules/filter/filter.module
+83
-0
core/modules/filter/lib/Drupal/filter/Tests/FilterHtmlImageSecureTest.php
...ter/lib/Drupal/filter/Tests/FilterHtmlImageSecureTest.php
+139
-0
No files found.
core/CHANGELOG.txt
View file @
6323f118
...
@@ -97,6 +97,7 @@ Drupal 8.0, xxxx-xx-xx (development version)
...
@@ -97,6 +97,7 @@ Drupal 8.0, xxxx-xx-xx (development version)
* Added language select form element in the Form API.
* Added language select form element in the Form API.
- Added E-mail field type to core.
- Added E-mail field type to core.
- Added Link field type to core.
- Added Link field type to core.
- Added local image input filter, to enable secure image posting.
Drupal 7.0, 2011-01-05
Drupal 7.0, 2011-01-05
----------------------
----------------------
...
...
core/modules/filter/filter.module
View file @
6323f118
...
@@ -73,6 +73,9 @@ function filter_theme() {
...
@@ -73,6 +73,9 @@ function filter_theme() {
'filter_guidelines'
=>
array
(
'filter_guidelines'
=>
array
(
'variables'
=>
array
(
'format'
=>
NULL
),
'variables'
=>
array
(
'format'
=>
NULL
),
),
),
'filter_html_image_secure_image'
=>
array
(
'variables'
=>
array
(
'image'
=>
NULL
),
),
);
);
}
}
...
@@ -1246,6 +1249,14 @@ function filter_filter_info() {
...
@@ -1246,6 +1249,14 @@ function filter_filter_info() {
),
),
'tips callback'
=>
'_filter_url_tips'
,
'tips callback'
=>
'_filter_url_tips'
,
);
);
$filters
[
'filter_html_image_secure'
]
=
array
(
'title'
=>
t
(
'Restrict images to this site'
),
'description'
=>
t
(
'Disallows usage of <img> tag sources that are not hosted on this site by replacing them with a placeholder image.'
),
'process callback'
=>
'_filter_html_image_secure_process'
,
'tips callback'
=>
'_filter_html_image_secure_tips'
,
// Supposed to run after other filters and before HTML corrector by default.
'weight'
=>
9
,
);
$filters
[
'filter_htmlcorrector'
]
=
array
(
$filters
[
'filter_htmlcorrector'
]
=
array
(
'title'
=>
t
(
'Correct faulty and chopped off HTML'
),
'title'
=>
t
(
'Correct faulty and chopped off HTML'
),
'process callback'
=>
'_filter_htmlcorrector'
,
'process callback'
=>
'_filter_htmlcorrector'
,
...
@@ -1765,6 +1776,78 @@ function _filter_html_escape_tips($filter, $format, $long = FALSE) {
...
@@ -1765,6 +1776,78 @@ function _filter_html_escape_tips($filter, $format, $long = FALSE) {
return
t
(
'No HTML tags allowed.'
);
return
t
(
'No HTML tags allowed.'
);
}
}
/**
* Process callback for local image filter.
*/
function
_filter_html_image_secure_process
(
$text
)
{
// Find the path (e.g. '/') to Drupal root.
$base_path
=
base_path
();
$base_path_length
=
drupal_strlen
(
$base_path
);
// Find the directory on the server where index.php resides.
$local_dir
=
DRUPAL_ROOT
.
'/'
;
$html_dom
=
filter_dom_load
(
$text
);
$images
=
$html_dom
->
getElementsByTagName
(
'img'
);
foreach
(
$images
as
$image
)
{
$src
=
$image
->
getAttribute
(
'src'
);
// Remove absolute URLs pointing to the local domain to prevent mixed
// content errors.
$image
->
setAttribute
(
'src'
,
preg_replace
(
'|^https?://'
.
$_SERVER
[
'HTTP_HOST'
]
.
'|'
,
''
,
$src
));
// Verify that $src starts with $base_path.
// This also ensures that external images cannot be referenced.
$src
=
$image
->
getAttribute
(
'src'
);
if
(
drupal_substr
(
$src
,
0
,
$base_path_length
)
===
$base_path
)
{
// Remove the $base_path to get the path relative to the Drupal root.
// Ensure the path refers to an actual image by prefixing the image source
// with the Drupal root and running getimagesize() on it.
$local_image_path
=
$local_dir
.
drupal_substr
(
$src
,
$base_path_length
);
if
(
@
getimagesize
(
$local_image_path
))
{
// The image has the right path. Erroneous images are dealt with below.
continue
;
}
}
// Replace an invalid image with an error indicator.
theme
(
'filter_html_image_secure_image'
,
array
(
'image'
=>
$image
));
}
$text
=
filter_dom_serialize
(
$html_dom
);
return
$text
;
}
/**
* Formats an image DOM element that has an invalid source.
*
* @param DOMElement $image
* An IMG node to format, parsed from the filtered text.
*
* @return void
* Unlike other theme functions, the passed in $image is altered by reference.
*
* @see _filter_html_image_secure_process()
* @ingroup themeable
*/
function
theme_filter_html_image_secure_image
(
&
$variables
)
{
$image
=
$variables
[
'image'
];
// Turn an invalid image into an error indicator.
$image
->
setAttribute
(
'src'
,
base_path
()
.
'core/misc/message-16-error.png'
);
$image
->
setAttribute
(
'alt'
,
t
(
'Image removed.'
));
$image
->
setAttribute
(
'title'
,
t
(
'This image has been removed. For security reasons, only images from the local domain are allowed.'
));
// Add a CSS class to aid in styling.
$class
=
(
$image
->
getAttribute
(
'class'
)
?
trim
(
$image
->
getAttribute
(
'class'
))
.
' '
:
''
);
$class
.
=
'filter-image-invalid'
;
$image
->
setAttribute
(
'class'
,
$class
);
}
/**
* Filter tips callback for secure HTML image filter.
*/
function
_filter_html_image_secure_tips
(
$filter
,
$format
,
$long
=
FALSE
)
{
return
t
(
'Only images hosted on this site may be used in <img> tags.'
);
}
/**
/**
* @} End of "defgroup standard_filters".
* @} End of "defgroup standard_filters".
*/
*/
...
...
core/modules/filter/lib/Drupal/filter/Tests/FilterHtmlImageSecureTest.php
0 → 100644
View file @
6323f118
<?php
/**
* @file
* Contains Drupal\filter\Tests\FilterHtmlImageSecureTest.
*/
namespace
Drupal\filter\Tests
;
use
Drupal\simpletest\WebTestBase
;
/**
* Tests restriction of IMG tags in HTML input.
*/
class
FilterHtmlImageSecureTest
extends
WebTestBase
{
/**
* Modules to enable.
*
* @var array
*/
public
static
$modules
=
array
(
'filter'
,
'node'
,
'comment'
);
public
static
function
getInfo
()
{
return
array
(
'name'
=>
'Local image input filter'
,
'description'
=>
'Tests restriction of IMG tags in HTML input.'
,
'group'
=>
'Filter'
,
);
}
function
setUp
()
{
parent
::
setUp
();
// Setup Filtered HTML text format.
$filtered_html_format
=
array
(
'format'
=>
'filtered_html'
,
'name'
=>
'Filtered HTML'
,
'filters'
=>
array
(
'filter_html'
=>
array
(
'status'
=>
1
,
'settings'
=>
array
(
'allowed_html'
=>
'<img> <a>'
,
),
),
'filter_autop'
=>
array
(
'status'
=>
1
,
),
'filter_html_image_secure'
=>
array
(
'status'
=>
1
,
),
),
);
$filtered_html_format
=
(
object
)
$filtered_html_format
;
filter_format_save
(
$filtered_html_format
);
// Setup users.
$this
->
checkPermissions
(
array
(),
TRUE
);
$this
->
web_user
=
$this
->
drupalCreateUser
(
array
(
'access content'
,
'access comments'
,
'post comments'
,
'skip comment approval'
,
filter_permission_name
(
$filtered_html_format
),
));
$this
->
drupalLogin
(
$this
->
web_user
);
// Setup a node to comment and test on.
$this
->
drupalCreateContentType
(
array
(
'type'
=>
'page'
,
'name'
=>
'Basic page'
));
$this
->
node
=
$this
->
drupalCreateNode
();
}
/**
* Tests removal of images having a non-local source.
*/
function
testImageSource
()
{
global
$base_url
;
$public_files_path
=
variable_get
(
'file_public_path'
,
conf_path
()
.
'/files'
);
$http_base_url
=
preg_replace
(
'/^https?/'
,
'http'
,
$base_url
);
$https_base_url
=
preg_replace
(
'/^https?/'
,
'https'
,
$base_url
);
$files_path
=
base_path
()
.
$public_files_path
;
$csrf_path
=
$public_files_path
.
'/'
.
implode
(
'/'
,
array_fill
(
0
,
substr_count
(
$public_files_path
,
'/'
)
+
1
,
'..'
));
$druplicon
=
'core/misc/druplicon.png'
;
$red_x_image
=
base_path
()
.
'core/misc/message-16-error.png'
;
$alt_text
=
t
(
'Image removed.'
);
$title_text
=
t
(
'This image has been removed. For security reasons, only images from the local domain are allowed.'
);
// Put a test image in the files directory.
$test_images
=
$this
->
drupalGetTestFiles
(
'image'
);
$test_image
=
$test_images
[
0
]
->
filename
;
// Create a list of test image sources.
// The keys become the value of the IMG 'src' attribute, the values are the
// expected filter conversions.
$images
=
array
(
$http_base_url
.
'/'
.
$druplicon
=>
base_path
()
.
$druplicon
,
$https_base_url
.
'/'
.
$druplicon
=>
base_path
()
.
$druplicon
,
base_path
()
.
$druplicon
=>
base_path
()
.
$druplicon
,
$files_path
.
'/'
.
$test_image
=>
$files_path
.
'/'
.
$test_image
,
$http_base_url
.
'/'
.
$public_files_path
.
'/'
.
$test_image
=>
$files_path
.
'/'
.
$test_image
,
$https_base_url
.
'/'
.
$public_files_path
.
'/'
.
$test_image
=>
$files_path
.
'/'
.
$test_image
,
$files_path
.
'/example.png'
=>
$red_x_image
,
'http://example.com/'
.
$druplicon
=>
$red_x_image
,
'https://example.com/'
.
$druplicon
=>
$red_x_image
,
'javascript:druplicon.png'
=>
$red_x_image
,
$csrf_path
.
'/logout'
=>
$red_x_image
,
);
$comment
=
array
();
foreach
(
$images
as
$image
=>
$converted
)
{
// Output the image source as plain text for debugging.
$comment
[]
=
$image
.
':'
;
// Hash the image source in a custom test attribute, because it might
// contain characters that confuse XPath.
$comment
[]
=
'<img src="'
.
$image
.
'" testattribute="'
.
md5
(
$image
)
.
'" />'
;
}
$edit
=
array
(
'comment_body[und][0][value]'
=>
implode
(
"
\n
"
,
$comment
),
);
$this
->
drupalPost
(
'node/'
.
$this
->
node
->
nid
,
$edit
,
t
(
'Save'
));
foreach
(
$images
as
$image
=>
$converted
)
{
$found
=
FALSE
;
foreach
(
$this
->
xpath
(
'//img[@testattribute="'
.
md5
(
$image
)
.
'"]'
)
as
$element
)
{
$found
=
TRUE
;
if
(
$converted
==
$red_x_image
)
{
$this
->
assertEqual
((
string
)
$element
[
'src'
],
$red_x_image
);
$this
->
assertEqual
((
string
)
$element
[
'alt'
],
$alt_text
);
$this
->
assertEqual
((
string
)
$element
[
'title'
],
$title_text
);
}
else
{
$this
->
assertEqual
((
string
)
$element
[
'src'
],
$converted
);
}
}
$this
->
assertTrue
(
$found
,
format_string
(
'@image was found.'
,
array
(
'@image'
=>
$image
)));
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment