citadel-core/app/lib/validate.py
2022-02-06 19:20:49 +01:00

104 lines
4.1 KiB
Python

# SPDX-FileCopyrightText: 2021-2022 Citadel and contributors
#
# SPDX-License-Identifier: GPL-3.0-or-later
import os
import yaml
from jsonschema import validate
import yaml
# Validates app data
# Returns true if valid, false otherwise
scriptDir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")
def validateApp(app: dict):
with open(os.path.join(scriptDir, 'app-standard-v1.yml'), 'r') as f:
schemaVersion1 = yaml.safe_load(f)
with open(os.path.join(scriptDir, 'app-standard-v2.yml'), 'r') as f:
schemaVersion2 = yaml.safe_load(f)
if 'version' in app and str(app['version']) == "1":
try:
validate(app, schemaVersion1)
return True
# Catch and log any errors, and return false
except Exception as e:
print(e)
return False
elif 'version' in app and str(app['version']) == "2":
try:
validate(app, schemaVersion2)
return True
# Catch and log any errors, and return false
except Exception as e:
print(e)
return False
else:
print("Unsupported app version")
return False
# Read in an app.yml file and pass it to the validation function
# Returns true if valid, false otherwise
def validateAppFile(file: str):
with open(file, 'r') as f:
return validateApp(yaml.safe_load(f))
# Returns all dirs in the dir
def findApps(dir: str):
apps = []
# Only get directories in the dir, but not recursively
for root, dirs, files in os.walk(dir, topdown=False):
for name in dirs:
app_dir = os.path.join(root, name)
if os.path.isfile(os.path.join(app_dir, "app.yml")) or os.path.isfile(os.path.join(app_dir, "docker-compose.yml")):
apps.append(name)
return apps
# Lists all folders in a directory and checks if they are valid
# A folder is valid if it contains an app.yml file
# A folder is invalid if it doesn't contain an app.yml file
def findAndValidateApps(dir: str):
apps = []
app_data = {}
for root, dirs, files in os.walk(dir, topdown=False):
for name in dirs:
app_dir = os.path.join(root, name)
if os.path.isfile(os.path.join(app_dir, "app.yml")):
apps.append(name)
# Read the app.yml and append it to app_data
with open(os.path.join(app_dir, "app.yml"), 'r') as f:
app_data[name] = yaml.safe_load(f)
# Now validate all the apps using the validateAppFile function by passing the app.yml as an argument to it, if an app is invalid, remove it from the list
for app in apps:
appyml = app_data[app]
if not validateApp(appyml):
apps.remove(app)
print("Warning: App {} is invalid".format(app))
# Skip to the next iteration of the loop
continue
# More security validation
should_continue=True
if appyml['metadata']['dependencies']:
for dependency in appyml['metadata']['dependencies']:
if dependency not in apps and dependency not in ["bitcoind", "lnd", "electrum"]:
print("WARNING: App '{}' has unknown dependency '{}'".format(app, dependency))
apps.remove(app)
should_continue=False
if dependency == app:
print("WARNING: App '{}' depends on itself".format(app))
apps.remove(app)
should_continue=False
if not should_continue:
continue
for container in appyml['containers']:
if 'permissions' in container:
for permission in container['permissions']:
if permission not in appyml['metadata']['dependencies'] and permission not in ["root", "hw"]:
print("WARNING: App {}'s container '{}' requires the '{}' permission, but the app doesn't list it in it's dependencies".format(app, container['name'], permission))
apps.remove(app)
# Skip to the next iteration of the loop
continue
return apps