Add seperate validation for blueprint names
The VALID_API_STRING function allows for characters that should not be allowed in blueprint names. VALID_BLUEPRINT_NAME allows us to specifically check if a blueprint contains a valid name.
This commit is contained in:
parent
e11fc2037f
commit
4174186c14
@ -19,3 +19,6 @@ import re
|
|||||||
# These are the characters that we allow to be passed in via the
|
# These are the characters that we allow to be passed in via the
|
||||||
# API calls.
|
# API calls.
|
||||||
VALID_API_STRING = re.compile(r'^[a-zA-Z0-9_,.:+*-]+$')
|
VALID_API_STRING = re.compile(r'^[a-zA-Z0-9_,.:+*-]+$')
|
||||||
|
|
||||||
|
# These are the characters that we allow to be used in blueprint names.
|
||||||
|
VALID_BLUEPRINT_NAME = re.compile(r'^[a-zA-Z0-9._-]+$')
|
@ -68,7 +68,7 @@ from pylorax.api.queue import uuid_tar, uuid_image, uuid_cancel, uuid_log
|
|||||||
from pylorax.api.recipes import RecipeError, list_branch_files, read_recipe_commit, recipe_filename, list_commits
|
from pylorax.api.recipes import RecipeError, list_branch_files, read_recipe_commit, recipe_filename, list_commits
|
||||||
from pylorax.api.recipes import recipe_from_dict, recipe_from_toml, commit_recipe, delete_recipe, revert_recipe
|
from pylorax.api.recipes import recipe_from_dict, recipe_from_toml, commit_recipe, delete_recipe, revert_recipe
|
||||||
from pylorax.api.recipes import tag_recipe_commit, recipe_diff, RecipeFileError
|
from pylorax.api.recipes import tag_recipe_commit, recipe_diff, RecipeFileError
|
||||||
from pylorax.api.regexes import VALID_API_STRING
|
from pylorax.api.regexes import VALID_API_STRING, VALID_BLUEPRINT_NAME
|
||||||
import pylorax.api.toml as toml
|
import pylorax.api.toml as toml
|
||||||
from pylorax.api.workspace import workspace_read, workspace_write, workspace_delete
|
from pylorax.api.workspace import workspace_read, workspace_write, workspace_delete
|
||||||
|
|
||||||
@ -211,7 +211,7 @@ def v0_blueprints_info(blueprint_names):
|
|||||||
"blueprints": []
|
"blueprints": []
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
if VALID_API_STRING.match(blueprint_names) is None:
|
if any(VALID_BLUEPRINT_NAME.match(blueprint_name) is None for blueprint_name in blueprint_names.split(',')):
|
||||||
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
|
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
|
||||||
|
|
||||||
branch = request.args.get("branch", "master")
|
branch = request.args.get("branch", "master")
|
||||||
@ -344,7 +344,7 @@ def v0_blueprints_changes(blueprint_names):
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
if VALID_API_STRING.match(blueprint_names) is None:
|
if any(VALID_BLUEPRINT_NAME.match(blueprint_name) is None for blueprint_name in blueprint_names.split(',')):
|
||||||
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
|
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
|
||||||
|
|
||||||
branch = request.args.get("branch", "master")
|
branch = request.args.get("branch", "master")
|
||||||
@ -402,7 +402,7 @@ def v0_blueprints_new():
|
|||||||
else:
|
else:
|
||||||
blueprint = recipe_from_dict(request.get_json(cache=False))
|
blueprint = recipe_from_dict(request.get_json(cache=False))
|
||||||
|
|
||||||
if VALID_API_STRING.match(blueprint["name"]) is None:
|
if VALID_BLUEPRINT_NAME.match(blueprint["name"]) is None:
|
||||||
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
|
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
|
||||||
|
|
||||||
with api.config["GITLOCK"].lock:
|
with api.config["GITLOCK"].lock:
|
||||||
@ -433,7 +433,7 @@ def v0_blueprints_delete(blueprint_name):
|
|||||||
The response will be a status response with `status` set to true, or an
|
The response will be a status response with `status` set to true, or an
|
||||||
error response with it set to false and an error message included.
|
error response with it set to false and an error message included.
|
||||||
"""
|
"""
|
||||||
if VALID_API_STRING.match(blueprint_name) is None:
|
if VALID_BLUEPRINT_NAME.match(blueprint_name) is None:
|
||||||
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
|
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
|
||||||
|
|
||||||
branch = request.args.get("branch", "master")
|
branch = request.args.get("branch", "master")
|
||||||
@ -476,7 +476,7 @@ def v0_blueprints_workspace():
|
|||||||
else:
|
else:
|
||||||
blueprint = recipe_from_dict(request.get_json(cache=False))
|
blueprint = recipe_from_dict(request.get_json(cache=False))
|
||||||
|
|
||||||
if VALID_API_STRING.match(blueprint["name"]) is None:
|
if VALID_BLUEPRINT_NAME.match(blueprint["name"]) is None:
|
||||||
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
|
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
|
||||||
|
|
||||||
with api.config["GITLOCK"].lock:
|
with api.config["GITLOCK"].lock:
|
||||||
@ -502,7 +502,7 @@ def v0_blueprints_delete_workspace(blueprint_name):
|
|||||||
The response will be a status response with `status` set to true, or an
|
The response will be a status response with `status` set to true, or an
|
||||||
error response with it set to false and an error message included.
|
error response with it set to false and an error message included.
|
||||||
"""
|
"""
|
||||||
if VALID_API_STRING.match(blueprint_name) is None:
|
if VALID_BLUEPRINT_NAME.match(blueprint_name) is None:
|
||||||
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
|
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
|
||||||
|
|
||||||
branch = request.args.get("branch", "master")
|
branch = request.args.get("branch", "master")
|
||||||
@ -534,7 +534,7 @@ def v0_blueprints_undo(blueprint_name, commit):
|
|||||||
The response will be a status response with `status` set to true, or an
|
The response will be a status response with `status` set to true, or an
|
||||||
error response with it set to false and an error message included.
|
error response with it set to false and an error message included.
|
||||||
"""
|
"""
|
||||||
if VALID_API_STRING.match(blueprint_name) is None:
|
if VALID_BLUEPRINT_NAME.match(blueprint_name) is None:
|
||||||
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
|
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
|
||||||
|
|
||||||
branch = request.args.get("branch", "master")
|
branch = request.args.get("branch", "master")
|
||||||
@ -570,7 +570,7 @@ def v0_blueprints_tag(blueprint_name):
|
|||||||
The response will be a status response with `status` set to true, or an
|
The response will be a status response with `status` set to true, or an
|
||||||
error response with it set to false and an error message included.
|
error response with it set to false and an error message included.
|
||||||
"""
|
"""
|
||||||
if VALID_API_STRING.match(blueprint_name) is None:
|
if VALID_BLUEPRINT_NAME.match(blueprint_name) is None:
|
||||||
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
|
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
|
||||||
|
|
||||||
branch = request.args.get("branch", "master")
|
branch = request.args.get("branch", "master")
|
||||||
@ -734,7 +734,7 @@ def v0_blueprints_freeze(blueprint_names):
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
if VALID_API_STRING.match(blueprint_names) is None:
|
if any(VALID_BLUEPRINT_NAME.match(blueprint_name) is None for blueprint_name in blueprint_names.split(',')):
|
||||||
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
|
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
|
||||||
|
|
||||||
branch = request.args.get("branch", "master")
|
branch = request.args.get("branch", "master")
|
||||||
@ -875,7 +875,7 @@ def v0_blueprints_depsolve(blueprint_names):
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
if VALID_API_STRING.match(blueprint_names) is None:
|
if any(VALID_BLUEPRINT_NAME.match(blueprint_name) is None for blueprint_name in blueprint_names.split(',')):
|
||||||
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
|
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
|
||||||
|
|
||||||
branch = request.args.get("branch", "master")
|
branch = request.args.get("branch", "master")
|
||||||
@ -1520,7 +1520,7 @@ def v0_compose_start():
|
|||||||
else:
|
else:
|
||||||
compose_type = compose["compose_type"]
|
compose_type = compose["compose_type"]
|
||||||
|
|
||||||
if VALID_API_STRING.match(blueprint_name) is None:
|
if VALID_BLUEPRINT_NAME.match(blueprint_name) is None:
|
||||||
errors.append({"id": INVALID_CHARS, "msg": "Invalid characters in API path"})
|
errors.append({"id": INVALID_CHARS, "msg": "Invalid characters in API path"})
|
||||||
|
|
||||||
if not blueprint_exists(branch, blueprint_name):
|
if not blueprint_exists(branch, blueprint_name):
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
import composertest
|
import composertest
|
||||||
import requests
|
import requests
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
class TestApi(composertest.ComposerTestCase):
|
class TestApi(composertest.ComposerTestCase):
|
||||||
@ -30,9 +31,9 @@ class TestApi(composertest.ComposerTestCase):
|
|||||||
self.forwarder_proc.kill()
|
self.forwarder_proc.kill()
|
||||||
super().tearDown()
|
super().tearDown()
|
||||||
|
|
||||||
def request(self, method, path, check=True):
|
def request(self, method, path, json=None, check=True):
|
||||||
self.assertEqual(path[0], "/")
|
self.assertEqual(path[0], "/")
|
||||||
r = requests.request(method, f"http://localhost:{self.composer_port}{path}", timeout=30)
|
r = requests.request(method, f"http://localhost:{self.composer_port}{path}", json=json, timeout=30)
|
||||||
if check:
|
if check:
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
return r
|
return r
|
||||||
@ -67,6 +68,24 @@ class TestApi(composertest.ComposerTestCase):
|
|||||||
"errors": [{ "id": "HTTPError", "code": 405, "msg": "Method Not Allowed" }]
|
"errors": [{ "id": "HTTPError", "code": 405, "msg": "Method Not Allowed" }]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
#
|
||||||
|
# API create blueprint with InvalidChars
|
||||||
|
#
|
||||||
|
invalid_blueprint = {
|
||||||
|
"name": "Name,With,Commas",
|
||||||
|
"description": "",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"modules": [],
|
||||||
|
"groups": []
|
||||||
|
}
|
||||||
|
|
||||||
|
r = self.request("POST", "/api/v0/blueprints/new", json=invalid_blueprint, check=False)
|
||||||
|
self.assertEqual(r.status_code, 400)
|
||||||
|
self.assertEqual(r.json(), {
|
||||||
|
"status": False,
|
||||||
|
"errors": [{ "id": "InvalidChars", "code": 400, "msg": "Invalid characters in API path" }]
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
composertest.main()
|
composertest.main()
|
||||||
|
Loading…
Reference in New Issue
Block a user