diff --git a/compose/config/config.py b/compose/config/config.py index f64dc04a0..84933e9c9 100644 --- a/compose/config/config.py +++ b/compose/config/config.py @@ -5,6 +5,7 @@ import functools import io import logging import os +import re import string import sys from collections import namedtuple @@ -214,6 +215,12 @@ class ConfigFile(namedtuple('_ConfigFile', 'filename config')): .format(self.filename, VERSION_EXPLANATION) ) + version_pattern = re.compile(r"^[2-9]+(\.\d+)?$") + if not version_pattern.match(version): + raise ConfigurationError( + 'Version "{}" in "{}" is invalid.' + .format(version, self.filename)) + if version == '2': return const.COMPOSEFILE_V2_0 diff --git a/tests/unit/config/config_test.py b/tests/unit/config/config_test.py index 0d3f49b99..0f744e22a 100644 --- a/tests/unit/config/config_test.py +++ b/tests/unit/config/config_test.py @@ -13,6 +13,8 @@ from random import shuffle import py import pytest import yaml +from ddt import data +from ddt import ddt from ...helpers import build_config_details from ...helpers import BUSYBOX_IMAGE_WITH_TAG @@ -68,6 +70,7 @@ def secret_sort(secrets): return sorted(secrets, key=itemgetter('source')) +@ddt class ConfigTest(unittest.TestCase): def test_load(self): @@ -1885,6 +1888,26 @@ class ConfigTest(unittest.TestCase): } ] + @data( + '2 ', + '3.', + '3.0.0', + '3.0.a', + '3.a', + '3a') + def test_invalid_version_formats(self, version): + content = { + 'version': version, + 'services': { + 'web': { + 'image': 'alpine', + } + } + } + with pytest.raises(ConfigurationError) as exc: + config.load(build_config_details(content)) + assert 'Version "{}" in "filename.yml" is invalid.'.format(version) in exc.exconly() + def test_group_add_option(self): actual = config.load(build_config_details({ 'version': '2',