Don't use os.system() in compilemessages.
Fixes #19584. This implies stop storing file path command line arguments in envvars as a security measure to start relying on with Popen's shell=False instead, and addition of an 'utils' module. Thanks kmichel_wgs for the report.
This commit is contained in:
parent
5c51d71f9a
commit
dfa9324966
|
@ -2,9 +2,10 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
import codecs
|
import codecs
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
from optparse import make_option
|
from optparse import make_option
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
|
from django.core.management.utils import popen_wrapper
|
||||||
from django.utils._os import npath
|
from django.utils._os import npath
|
||||||
|
|
||||||
def has_bom(fn):
|
def has_bom(fn):
|
||||||
|
@ -41,18 +42,15 @@ def compile_messages(stderr, locale=None):
|
||||||
if has_bom(fn):
|
if has_bom(fn):
|
||||||
raise CommandError("The %s file has a BOM (Byte Order Mark). Django only supports .po files encoded in UTF-8 and without any BOM." % fn)
|
raise CommandError("The %s file has a BOM (Byte Order Mark). Django only supports .po files encoded in UTF-8 and without any BOM." % fn)
|
||||||
pf = os.path.splitext(fn)[0]
|
pf = os.path.splitext(fn)[0]
|
||||||
# Store the names of the .mo and .po files in an environment
|
program = 'msgfmt'
|
||||||
# variable, rather than doing a string replacement into the
|
args = [program, '--check-format', '-o', npath(pf + '.mo'), npath(pf + '.po')]
|
||||||
# command, so that we can take advantage of shell quoting, to
|
output, errors, status = popen_wrapper(args)
|
||||||
# quote any malicious characters/escaping.
|
if status:
|
||||||
# See http://cyberelk.net/tim/articles/cmdline/ar01s02.html
|
if errors:
|
||||||
os.environ['djangocompilemo'] = npath(pf + '.mo')
|
msg = "Execution of %s failed: %s" % (program, errors)
|
||||||
os.environ['djangocompilepo'] = npath(pf + '.po')
|
else:
|
||||||
if sys.platform == 'win32': # Different shell-variable syntax
|
msg = "Execution of %s failed" % program
|
||||||
cmd = 'msgfmt --check-format -o "%djangocompilemo%" "%djangocompilepo%"'
|
raise CommandError(msg)
|
||||||
else:
|
|
||||||
cmd = 'msgfmt --check-format -o "$djangocompilemo" "$djangocompilepo"'
|
|
||||||
os.system(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import os
|
||||||
|
from subprocess import PIPE, Popen
|
||||||
|
|
||||||
|
|
||||||
|
def popen_wrapper(args):
|
||||||
|
"""
|
||||||
|
Friendly wrapper around Popen.
|
||||||
|
|
||||||
|
Returns stdout output, stderr output and OS status code.
|
||||||
|
"""
|
||||||
|
p = Popen(args, shell=False, stdout=PIPE, stderr=PIPE,
|
||||||
|
close_fds=os.name != 'nt', universal_newlines=True)
|
||||||
|
output, errors = p.communicate()
|
||||||
|
return output, errors, p.returncode
|
|
@ -99,3 +99,22 @@ class MultipleLocaleCompilationTests(MessageCompilationTests):
|
||||||
|
|
||||||
self.assertTrue(os.path.exists(self.MO_FILE_HR))
|
self.assertTrue(os.path.exists(self.MO_FILE_HR))
|
||||||
self.assertTrue(os.path.exists(self.MO_FILE_FR))
|
self.assertTrue(os.path.exists(self.MO_FILE_FR))
|
||||||
|
|
||||||
|
|
||||||
|
class CompilationErrorHandling(MessageCompilationTests):
|
||||||
|
|
||||||
|
LOCALE='ja'
|
||||||
|
MO_FILE='locale/%s/LC_MESSAGES/django.mo' % LOCALE
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(CompilationErrorHandling, self).setUp()
|
||||||
|
self.addCleanup(self._rmfile, os.path.join(test_dir, self.MO_FILE))
|
||||||
|
|
||||||
|
def _rmfile(self, filepath):
|
||||||
|
if os.path.exists(filepath):
|
||||||
|
os.remove(filepath)
|
||||||
|
|
||||||
|
def test_error_reported_by_msgfmt(self):
|
||||||
|
os.chdir(test_dir)
|
||||||
|
with self.assertRaises(CommandError):
|
||||||
|
call_command('compilemessages', locale=self.LOCALE, stderr=StringIO())
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||||
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2011-12-04 04:59-0600\n"
|
||||||
|
"PO-Revision-Date: 2013-02-26 21:29-0300\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"Language: ja\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||||
|
|
||||||
|
#, brainfuck-format
|
||||||
|
msgwhat!? "This is an invalid PO file. GNU msgfmt should reject it."
|
|
@ -41,7 +41,8 @@ if can_run_extraction_tests:
|
||||||
MultipleLocaleExtractionTests)
|
MultipleLocaleExtractionTests)
|
||||||
if can_run_compilation_tests:
|
if can_run_compilation_tests:
|
||||||
from .commands.compilation import (PoFileTests, PoFileContentsTests,
|
from .commands.compilation import (PoFileTests, PoFileContentsTests,
|
||||||
PercentRenderingTests, MultipleLocaleCompilationTests)
|
PercentRenderingTests, MultipleLocaleCompilationTests,
|
||||||
|
CompilationErrorHandling)
|
||||||
from .contenttypes.tests import ContentTypeTests
|
from .contenttypes.tests import ContentTypeTests
|
||||||
from .forms import I18nForm, SelectDateForm, SelectDateWidget, CompanyForm
|
from .forms import I18nForm, SelectDateForm, SelectDateWidget, CompanyForm
|
||||||
from .models import Company, TestModel
|
from .models import Company, TestModel
|
||||||
|
|
Loading…
Reference in New Issue