mirror of
https://github.com/OCA/reporting-engine.git
synced 2025-02-16 16:30:38 +02:00
[IMP] report_py3o: prevent injections when retrieving the template from path
This commit is contained in:
committed by
Elmeri Niemelä
parent
ce615f24f6
commit
4e96f362f0
@@ -6,10 +6,13 @@ from base64 import b64decode
|
||||
import mock
|
||||
import os
|
||||
import pkg_resources
|
||||
import shutil
|
||||
import tempfile
|
||||
from contextlib import contextmanager
|
||||
|
||||
from py3o.formats import Formats
|
||||
|
||||
from odoo import tools
|
||||
from odoo.tests.common import TransactionCase
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
@@ -17,13 +20,29 @@ from ..models.py3o_report import TemplateNotFound
|
||||
from base64 import b64encode
|
||||
|
||||
|
||||
@contextmanager
|
||||
def temporary_copy(path):
|
||||
filname, ext = os.path.splitext(path)
|
||||
tmp_filename = tempfile.mktemp(suffix='.' + ext)
|
||||
try:
|
||||
shutil.copy2(path, tmp_filename)
|
||||
yield tmp_filename
|
||||
finally:
|
||||
os.unlink(tmp_filename)
|
||||
|
||||
|
||||
class TestReportPy3o(TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestReportPy3o, self).setUp()
|
||||
self.report = self.env.ref("report_py3o.res_users_report_py3o")
|
||||
self.py3o_report = self.env['py3o.report'].create({
|
||||
'ir_actions_report_xml_id': self.report.id})
|
||||
|
||||
def test_no_local_fusion_without_fusion_server(self):
|
||||
report = self.env.ref("report_py3o.res_users_report_py3o")
|
||||
self.assertTrue(report.py3o_is_local_fusion)
|
||||
self.assertTrue(self.report.py3o_is_local_fusion)
|
||||
with self.assertRaises(ValidationError) as e:
|
||||
report.py3o_is_local_fusion = False
|
||||
self.report.py3o_is_local_fusion = False
|
||||
self.assertEqual(
|
||||
e.exception.name,
|
||||
"Can not use not native format in local fusion. "
|
||||
@@ -49,17 +68,15 @@ class TestReportPy3o(TransactionCase):
|
||||
"Please specify a Fusion Server")
|
||||
|
||||
def test_required_py3_filetype(self):
|
||||
report = self.env.ref("report_py3o.res_users_report_py3o")
|
||||
self.assertEqual(report.report_type, "py3o")
|
||||
self.assertEqual(self.report.report_type, "py3o")
|
||||
with self.assertRaises(ValidationError) as e:
|
||||
report.py3o_filetype = False
|
||||
self.report.py3o_filetype = False
|
||||
self.assertEqual(
|
||||
e.exception.name,
|
||||
"Field 'Output Format' is required for Py3O report")
|
||||
|
||||
def test_reports(self):
|
||||
py3o_report = self.env['py3o.report']
|
||||
report = self.env.ref("report_py3o.res_users_report_py3o")
|
||||
with mock.patch.object(
|
||||
py3o_report.__class__, '_create_single_report') as patched_pdf:
|
||||
result = tempfile.mktemp('.txt')
|
||||
@@ -67,26 +84,26 @@ class TestReportPy3o(TransactionCase):
|
||||
fp.write('dummy')
|
||||
patched_pdf.return_value = result
|
||||
# test the call the the create method inside our custom parser
|
||||
report.render_report(self.env.user.ids,
|
||||
report.report_name,
|
||||
{})
|
||||
self.report.render_report(self.env.user.ids,
|
||||
self.report.report_name,
|
||||
{})
|
||||
self.assertEqual(1, patched_pdf.call_count)
|
||||
# generated files no more exists
|
||||
self.assertFalse(os.path.exists(result))
|
||||
res = report.render_report(
|
||||
self.env.user.ids, report.report_name, {})
|
||||
res = self.report.render_report(
|
||||
self.env.user.ids, self.report.report_name, {})
|
||||
self.assertTrue(res)
|
||||
py3o_server = self.env['py3o.server'].create({"url": "http://dummy"})
|
||||
# check the call to the fusion server
|
||||
report.write({"py3o_filetype": "pdf",
|
||||
"py3o_server_id": py3o_server.id})
|
||||
self.report.write({"py3o_filetype": "pdf",
|
||||
"py3o_server_id": py3o_server.id})
|
||||
with mock.patch('requests.post') as patched_post:
|
||||
magick_response = mock.MagicMock()
|
||||
magick_response.status_code = 200
|
||||
patched_post.return_value = magick_response
|
||||
magick_response.iter_content.return_value = "test result"
|
||||
res = report.render_report(
|
||||
self.env.user.ids, report.report_name, {})
|
||||
res = self.report.render_report(
|
||||
self.env.user.ids, self.report.report_name, {})
|
||||
self.assertEqual(('test result', 'pdf'), res)
|
||||
|
||||
def test_report_post_process(self):
|
||||
@@ -118,31 +135,38 @@ class TestReportPy3o(TransactionCase):
|
||||
self.assertEqual('test result', b64decode(attachements.datas))
|
||||
|
||||
def test_report_template_configs(self):
|
||||
report = self.env.ref("report_py3o.res_users_report_py3o")
|
||||
# the demo template is specified with a relative path in in the module
|
||||
# path
|
||||
tmpl_name = report.py3o_template_fallback
|
||||
tmpl_name = self.report.py3o_template_fallback
|
||||
flbk_filename = pkg_resources.resource_filename(
|
||||
"odoo.addons.%s" % report.module,
|
||||
"odoo.addons.%s" % self.report.module,
|
||||
tmpl_name)
|
||||
self.assertTrue(os.path.exists(flbk_filename))
|
||||
res = report.render_report(
|
||||
self.env.user.ids, report.report_name, {})
|
||||
res = self.report.render_report(
|
||||
self.env.user.ids, self.report.report_name, {})
|
||||
self.assertTrue(res)
|
||||
# The generation fails if the tempalte is not found
|
||||
report.module = False
|
||||
self.report.module = False
|
||||
with self.assertRaises(TemplateNotFound), self.env.cr.savepoint():
|
||||
report.render_report(
|
||||
self.env.user.ids, report.report_name, {})
|
||||
self.report.render_report(
|
||||
self.env.user.ids, self.report.report_name, {})
|
||||
|
||||
# the template can also be provided as an abspaath
|
||||
report.py3o_template_fallback = flbk_filename
|
||||
res = report.render_report(
|
||||
self.env.user.ids, report.report_name, {})
|
||||
self.assertTrue(res)
|
||||
# the template can also be provided as an abspath if it's root path
|
||||
# is trusted
|
||||
self.report.py3o_template_fallback = flbk_filename
|
||||
with self.assertRaises(TemplateNotFound):
|
||||
self.report.render_report(
|
||||
self.env.user.ids, self.report.report_name, {})
|
||||
with temporary_copy(flbk_filename) as tmp_filename:
|
||||
self.report.py3o_template_fallback = tmp_filename
|
||||
tools.config.misc['report_py3o'] = {
|
||||
'root_tmpl_path': os.path.dirname(tmp_filename)}
|
||||
res = self.report.render_report(
|
||||
self.env.user.ids, self.report.report_name, {})
|
||||
self.assertTrue(res)
|
||||
|
||||
# the tempalte can also be provided as a binay field
|
||||
report.py3o_template_fallback = False
|
||||
self.report.py3o_template_fallback = False
|
||||
|
||||
with open(flbk_filename) as tmpl_file:
|
||||
tmpl_data = b64encode(tmpl_file.read())
|
||||
@@ -150,8 +174,40 @@ class TestReportPy3o(TransactionCase):
|
||||
'name': 'test_template',
|
||||
'py3o_template_data': tmpl_data,
|
||||
'filetype': 'odt'})
|
||||
report.py3o_template_id = py3o_template
|
||||
report.py3o_template_fallback = flbk_filename
|
||||
res = report.render_report(
|
||||
self.env.user.ids, report.report_name, {})
|
||||
self.report.py3o_template_id = py3o_template
|
||||
self.report.py3o_template_fallback = flbk_filename
|
||||
res = self.report.render_report(
|
||||
self.env.user.ids, self.report.report_name, {})
|
||||
self.assertTrue(res)
|
||||
|
||||
def test_report_template_fallback_validity(self):
|
||||
tmpl_name = self.report.py3o_template_fallback
|
||||
flbk_filename = pkg_resources.resource_filename(
|
||||
"odoo.addons.%s" % self.report.module,
|
||||
tmpl_name)
|
||||
# an exising file in a native format is a valid template if it's
|
||||
self.assertTrue(self.py3o_report._get_template_from_path(
|
||||
tmpl_name))
|
||||
self.report.module = None
|
||||
# a directory is not a valid template..
|
||||
self.assertFalse(self.py3o_report._get_template_from_path('/etc/'))
|
||||
self.assertFalse(self.py3o_report._get_template_from_path('.'))
|
||||
# an vaild template outside the root_tmpl_path is not a valid template
|
||||
# path
|
||||
# located in trusted directory
|
||||
self.report.py3o_template_fallback = flbk_filename
|
||||
self.assertFalse(self.py3o_report._get_template_from_path(
|
||||
flbk_filename))
|
||||
with temporary_copy(flbk_filename) as tmp_filename:
|
||||
self.assertTrue(self.py3o_report._get_template_from_path(
|
||||
tmp_filename))
|
||||
# check security
|
||||
self.assertFalse(self.py3o_report._get_template_from_path(
|
||||
'rm -rf . & %s' % flbk_filename))
|
||||
# a file in a non native LibreOffice format is not a valid template
|
||||
with tempfile.NamedTemporaryFile(suffix='.toto')as f:
|
||||
self.assertFalse(self.py3o_report._get_template_from_path(
|
||||
f.name))
|
||||
# non exising files are not valid template
|
||||
self.assertFalse(self.py3o_report._get_template_from_path(
|
||||
'/etc/test.odt'))
|
||||
|
||||
Reference in New Issue
Block a user