feat(deploy): reliability API routes, atomic releases, version single-source
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
"""UCI profile snapshots under /mmc/mk8/profiles/<name>/{pineapd,wireless,network}"""
|
||||
import os, time
|
||||
import os, re, time
|
||||
|
||||
PROFILES_DIR = '/mmc/mk8/profiles'
|
||||
CONFIGS = ('pineapd', 'wireless', 'network')
|
||||
NAME_RE = re.compile(r'^[A-Za-z0-9._-]{1,64}$')
|
||||
|
||||
|
||||
def run_cmd(args, timeout=20, input_data=None):
|
||||
@@ -12,6 +13,12 @@ def run_cmd(args, timeout=20, input_data=None):
|
||||
|
||||
|
||||
def _path(name):
|
||||
"""Resolve a profile name to its directory. HTTP-supplied names are never
|
||||
trusted: reject anything but [A-Za-z0-9._-]{1,64} and explicitly refuse
|
||||
'.'/'..' so traversal can never escape PROFILES_DIR."""
|
||||
if not isinstance(name, str) or not NAME_RE.match(name) \
|
||||
or name in ('.', '..'):
|
||||
raise ValueError('invalid profile name')
|
||||
return os.path.join(PROFILES_DIR, name)
|
||||
|
||||
|
||||
@@ -38,8 +45,14 @@ def auto_name(op):
|
||||
|
||||
def list_profiles():
|
||||
try:
|
||||
return sorted(d for d in os.listdir(PROFILES_DIR)
|
||||
if os.path.isdir(_path(d)))
|
||||
out = []
|
||||
for d in os.listdir(PROFILES_DIR):
|
||||
try:
|
||||
if os.path.isdir(_path(d)):
|
||||
out.append(d)
|
||||
except ValueError:
|
||||
continue
|
||||
return sorted(out)
|
||||
except OSError:
|
||||
return []
|
||||
|
||||
|
||||
Reference in New Issue
Block a user