mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
Merge branch 'dev' into lint-helpers
This commit is contained in:
commit
10a0496209
12 changed files with 336 additions and 176 deletions
|
@ -208,6 +208,8 @@ ynh_package_install_from_equivs() {
|
|||
ynh_package_is_installed "$pkgname"
|
||||
}
|
||||
|
||||
YNH_INSTALL_APP_DEPENDENCIES_REPLACE="true"
|
||||
|
||||
# Define and install dependencies with a equivs control file
|
||||
#
|
||||
# This helper can/should only be called once per app
|
||||
|
@ -245,19 +247,57 @@ ynh_install_app_dependencies() {
|
|||
dependencies="$(echo "$dependencies" | sed 's/\([^(\<=\>]\)\([\<=\>]\+\)\([^,]\+\)/\1 (\2 \3)/g')"
|
||||
fi
|
||||
|
||||
# Check for specific php dependencies which requires sury
|
||||
# This grep will for example return "7.4" if dependencies is "foo bar php7.4-pwet php-gni"
|
||||
# The (?<=php) syntax corresponds to lookbehind ;)
|
||||
local specific_php_version=$(echo $dependencies | grep -oP '(?<=php)[0-9.]+(?=-|\>)' | sort -u)
|
||||
|
||||
# Ignore case where the php version found is the one available in debian vanilla
|
||||
[[ "$specific_php_version" != "$YNH_DEFAULT_PHP_VERSION" ]] || specific_php_version=""
|
||||
|
||||
if [[ -n "$specific_php_version" ]]
|
||||
then
|
||||
# Cover a small edge case where a packager could have specified "php7.4-pwet php5-gni" which is confusing
|
||||
[[ $(echo $specific_php_version | wc -l) -eq 1 ]] \
|
||||
|| ynh_die --message="Inconsistent php versions in dependencies ... found : $specific_php_version"
|
||||
|
||||
dependencies+=", php${specific_php_version}, php${specific_php_version}-fpm, php${specific_php_version}-common"
|
||||
|
||||
ynh_add_sury
|
||||
fi
|
||||
|
||||
|
||||
# The first time we run ynh_install_app_dependencies, we will replace the
|
||||
# entire control file (This is in particular meant to cover the case of
|
||||
# upgrade script where ynh_install_app_dependencies is called with this
|
||||
# expected effect) Otherwise, any subsequent call will add dependencies
|
||||
# to those already present in the equivs control file.
|
||||
if [[ $YNH_INSTALL_APP_DEPENDENCIES_REPLACE == "true" ]]
|
||||
then
|
||||
YNH_INSTALL_APP_DEPENDENCIES_REPLACE="false"
|
||||
else
|
||||
local current_dependencies=""
|
||||
if ynh_package_is_installed --package="${dep_app}-ynh-deps"
|
||||
then
|
||||
current_dependencies="$(dpkg-query --show --showformat='${Depends}' ${dep_app}-ynh-deps) "
|
||||
current_dependencies=${current_dependencies// | /|}
|
||||
fi
|
||||
dependencies="$current_dependencies, $dependencies"
|
||||
fi
|
||||
|
||||
#
|
||||
# Epic ugly hack to fix the goddamn dependency nightmare of sury
|
||||
# Sponsored by the "Djeezusse Fokin Kraiste Why Do Adminsys Has To Be So Fucking Complicated I Should Go Grow Potatoes Instead Of This Shit" collective
|
||||
# https://github.com/YunoHost/issues/issues/1407
|
||||
#
|
||||
# If we require to install php dependency
|
||||
if echo $dependencies | grep --quiet 'php'; then
|
||||
if grep --quiet 'php' <<< "$dependencies"; then
|
||||
# And we have packages from sury installed (7.0.33-10+weirdshiftafter instead of 7.0.33-0 on debian)
|
||||
if dpkg --list | grep "php7.0" | grep --quiet --invert-match "7.0.33-0+deb9"; then
|
||||
# And sury ain't already in sources.lists
|
||||
if ! grep --recursive --quiet "^ *deb.*sury" /etc/apt/sources.list*; then
|
||||
# Re-add sury
|
||||
ynh_install_extra_repo --repo="https://packages.sury.org/php/ $(ynh_get_debian_release) main" --key="https://packages.sury.org/php/apt.gpg" --name=extra_php_version --priority=600
|
||||
ynh_add_sury
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
@ -275,14 +315,43 @@ EOF
|
|||
ynh_package_install_from_equivs /tmp/${dep_app}-ynh-deps.control \
|
||||
|| ynh_die --message="Unable to install dependencies" # Install the fake package and its dependencies
|
||||
rm /tmp/${dep_app}-ynh-deps.control
|
||||
|
||||
ynh_app_setting_set --app=$app --key=apt_dependencies --value="$dependencies"
|
||||
|
||||
if [[ -n "$specific_php_version" ]]
|
||||
then
|
||||
# Set the default php version back as the default version for php-cli.
|
||||
update-alternatives --set php /usr/bin/php$YNH_DEFAULT_PHP_VERSION
|
||||
|
||||
# Store phpversion into the config of this app
|
||||
ynh_app_setting_set --app=$app --key=phpversion --value=$specific_php_version
|
||||
|
||||
# Integrate new php-fpm service in yunohost
|
||||
yunohost service add php${specific_php_version}-fpm --log "/var/log/php${phpversion}-fpm.log"
|
||||
elif grep --quiet 'php' <<< "$dependencies"; then
|
||||
# Store phpversion into the config of this app
|
||||
ynh_app_setting_set --app=$app --key=phpversion --value=$YNH_DEFAULT_PHP_VERSION
|
||||
fi
|
||||
}
|
||||
|
||||
# Add sury repository with adequate pin strategy
|
||||
#
|
||||
# [internal]
|
||||
#
|
||||
# usage: ynh_add_sury
|
||||
#
|
||||
ynh_add_sury() {
|
||||
|
||||
# Add an extra repository for those packages
|
||||
ynh_install_extra_repo --repo="https://packages.sury.org/php/ $(ynh_get_debian_release) main" --key="https://packages.sury.org/php/apt.gpg" --name=extra_php_version --priority=600
|
||||
|
||||
}
|
||||
|
||||
|
||||
# Add dependencies to install with ynh_install_app_dependencies
|
||||
#
|
||||
# usage: ynh_add_app_dependencies --package=phpversion [--replace]
|
||||
# | arg: -p, --package= - Packages to add as dependencies for the app.
|
||||
# | arg: -r, --replace - Replace dependencies instead of adding to existing ones.
|
||||
#
|
||||
# Requires YunoHost version 3.8.1 or higher.
|
||||
ynh_add_app_dependencies() {
|
||||
|
@ -290,22 +359,11 @@ ynh_add_app_dependencies() {
|
|||
local legacy_args=pr
|
||||
local -A args_array=([p]=package= [r]=replace)
|
||||
local package
|
||||
local replace
|
||||
# Manage arguments with getopts
|
||||
ynh_handle_getopts_args "$@"
|
||||
replace=${replace:-0}
|
||||
|
||||
local current_dependencies=""
|
||||
if [ $replace -eq 0 ]; then
|
||||
local dep_app=${app//_/-} # Replace all '_' by '-'
|
||||
if ynh_package_is_installed --package="${dep_app}-ynh-deps"; then
|
||||
current_dependencies="$(dpkg-query --show --showformat='${Depends}' ${dep_app}-ynh-deps) "
|
||||
fi
|
||||
|
||||
current_dependencies=${current_dependencies// | /|}
|
||||
fi
|
||||
|
||||
ynh_install_app_dependencies "${current_dependencies}${package}"
|
||||
ynh_print_warn --message="Packagers: ynh_add_app_dependencies is deprecated and is now only an alias to ynh_install_app_dependencies"
|
||||
ynh_install_app_dependencies "${package}"
|
||||
}
|
||||
|
||||
# Remove fake package and its dependencies
|
||||
|
@ -316,8 +374,25 @@ ynh_add_app_dependencies() {
|
|||
#
|
||||
# Requires YunoHost version 2.6.4 or higher.
|
||||
ynh_remove_app_dependencies() {
|
||||
local dep_app=${app//_/-} # Replace all '_' by '-'
|
||||
ynh_package_autopurge ${dep_app}-ynh-deps # Remove the fake package and its dependencies if they not still used.
|
||||
local dep_app=${app//_/-} # Replace all '_' by '-'
|
||||
|
||||
local current_dependencies=""
|
||||
if ynh_package_is_installed --package="${dep_app}-ynh-deps"; then
|
||||
current_dependencies="$(dpkg-query --show --showformat='${Depends}' ${dep_app}-ynh-deps) "
|
||||
current_dependencies=${current_dependencies// | /|}
|
||||
fi
|
||||
|
||||
ynh_package_autopurge ${dep_app}-ynh-deps # Remove the fake package and its dependencies if they not still used.
|
||||
|
||||
# Check if this app used a specific php version ... in which case we check
|
||||
# if the corresponding php-fpm is still there. Otherwise, we remove the
|
||||
# service from yunohost as well
|
||||
|
||||
local specific_php_version=$(echo $current_dependencies | tr '-' ' ' | grep -o -E "\<php[0-9.]+\>" | sed 's/php//g' | sort | uniq)
|
||||
[[ "$specific_php_version" != "$YNH_DEFAULT_PHP_VERSION" ]] || specific_php_version=""
|
||||
if [[ -n "$specific_php_version" ]] && ! ynh_package_is_installed --package="php${specific_php_version}-fpm"; then
|
||||
yunohost service remove php${specific_php_version}-fpm
|
||||
fi
|
||||
}
|
||||
|
||||
# Install packages from an extra repository properly.
|
||||
|
@ -350,7 +425,7 @@ ynh_install_extra_app_dependencies() {
|
|||
ynh_install_extra_repo --repo="$repo" $key --priority=995 --name=$name
|
||||
|
||||
# Install requested dependencies from this extra repository.
|
||||
ynh_add_app_dependencies --package="$package"
|
||||
ynh_install_app_dependencies "$package"
|
||||
|
||||
# Remove this extra repository after packages are installed
|
||||
ynh_remove_extra_repo --name=$app
|
||||
|
|
|
@ -90,6 +90,11 @@ $logfile {
|
|||
EOF
|
||||
mkdir --parents $(dirname "$logfile") # Create the log directory, if not exist
|
||||
cat ${app}-logrotate | $customtee /etc/logrotate.d/$app >/dev/null # Append this config to the existing config file, or replace the whole config file (depending on $customtee)
|
||||
|
||||
if ynh_user_exists --username="$app"; then
|
||||
chown $app:$app "$logfile"
|
||||
chmod o-rwx "$logfile"
|
||||
fi
|
||||
}
|
||||
|
||||
# Remove the app's logrotate config.
|
||||
|
|
|
@ -107,7 +107,7 @@ ynh_add_fpm_config() {
|
|||
ynh_install_php --phpversion="$phpversion" "$additionnal_packages"
|
||||
elif [ -n "$package" ]; then
|
||||
# Install the additionnal packages from the default repository
|
||||
ynh_add_app_dependencies --package="$package"
|
||||
ynh_install_app_dependencies "$package"
|
||||
fi
|
||||
|
||||
if [ $dedicated_service -eq 1 ]; then
|
||||
|
@ -313,35 +313,12 @@ ynh_install_php() {
|
|||
ynh_handle_getopts_args "$@"
|
||||
package=${package:-}
|
||||
|
||||
# Store phpversion into the config of this app
|
||||
ynh_app_setting_set $app phpversion $phpversion
|
||||
|
||||
if [ "$phpversion" == "$YNH_DEFAULT_PHP_VERSION" ]; then
|
||||
ynh_die --message="Do not use ynh_install_php to install php$YNH_DEFAULT_PHP_VERSION"
|
||||
fi
|
||||
|
||||
# Create the file if doesn't exist already
|
||||
touch /etc/php/ynh_app_version
|
||||
|
||||
# Do not add twice the same line
|
||||
if ! grep --quiet "$YNH_APP_INSTANCE_NAME:" "/etc/php/ynh_app_version"; then
|
||||
# Store the ID of this app and the version of PHP requested for it
|
||||
echo "$YNH_APP_INSTANCE_NAME:$phpversion" | tee --append "/etc/php/ynh_app_version"
|
||||
fi
|
||||
|
||||
# Add an extra repository for those packages
|
||||
ynh_install_extra_repo --repo="https://packages.sury.org/php/ $(ynh_get_debian_release) main" --key="https://packages.sury.org/php/apt.gpg" --name=extra_php_version --priority=600
|
||||
|
||||
# Install requested dependencies from this extra repository.
|
||||
# Install PHP-FPM first, otherwise PHP will install apache as a dependency.
|
||||
ynh_add_app_dependencies --package="php${phpversion}-fpm"
|
||||
ynh_add_app_dependencies --package="php$phpversion php${phpversion}-common $package"
|
||||
|
||||
# Set the default PHP version back as the default version for php-cli.
|
||||
update-alternatives --set php /usr/bin/php$YNH_DEFAULT_PHP_VERSION
|
||||
|
||||
# Advertise service in admin panel
|
||||
yunohost service add php${phpversion}-fpm --log "/var/log/php${phpversion}-fpm.log"
|
||||
ynh_install_app_dependencies "$package"
|
||||
ynh_app_setting_set --app=$app --key=phpversion --value=$specific_php_version
|
||||
}
|
||||
|
||||
# Remove the specific version of PHP used by the app.
|
||||
|
@ -351,33 +328,8 @@ ynh_install_php() {
|
|||
# usage: ynh_install_php
|
||||
#
|
||||
# Requires YunoHost version 3.8.1 or higher.
|
||||
ynh_remove_php() {
|
||||
# Get the version of PHP used by this app
|
||||
local phpversion=$(ynh_app_setting_get --app=$app --key=phpversion)
|
||||
|
||||
if [ "$phpversion" == "$YNH_DEFAULT_PHP_VERSION" ] || [ -z "$phpversion" ]; then
|
||||
if [ "$phpversion" == "$YNH_DEFAULT_PHP_VERSION" ]; then
|
||||
ynh_print_err "Do not use ynh_remove_php to remove php$YNH_DEFAULT_PHP_VERSION !"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Create the file if doesn't exist already
|
||||
touch /etc/php/ynh_app_version
|
||||
|
||||
# Remove the line for this app
|
||||
sed --in-place "/$YNH_APP_INSTANCE_NAME:$phpversion/d" "/etc/php/ynh_app_version"
|
||||
|
||||
# If no other app uses this version of PHP, remove it.
|
||||
if ! grep --quiet "$phpversion" "/etc/php/ynh_app_version"; then
|
||||
# Remove the service from the admin panel
|
||||
if ynh_package_is_installed --package="php${phpversion}-fpm"; then
|
||||
yunohost service remove php${phpversion}-fpm
|
||||
fi
|
||||
|
||||
# Purge PHP dependencies for this version.
|
||||
ynh_package_autopurge "php$phpversion php${phpversion}-fpm php${phpversion}-common"
|
||||
fi
|
||||
ynh_remove_php () {
|
||||
ynh_remove_app_dependencies
|
||||
}
|
||||
|
||||
# Define the values to configure PHP-FPM
|
||||
|
|
|
@ -699,6 +699,28 @@ properly with chmod/chown."
|
|||
echo $TMP_DIR
|
||||
}
|
||||
|
||||
_acceptable_path_to_delete() {
|
||||
local file=$1
|
||||
|
||||
local forbidden_paths=$(ls -d / /* /{var,home,usr}/* /etc/{default,sudoers.d,yunohost,cron*})
|
||||
|
||||
# Legacy : A couple apps still have data in /home/$app ...
|
||||
if [[ -n "$app" ]]
|
||||
then
|
||||
forbidden_paths=$(echo "$forbidden_paths" | grep -v "/home/$app")
|
||||
fi
|
||||
|
||||
# Use realpath to normalize the path ..
|
||||
# i.e convert ///foo//bar//..///baz//// to /foo/baz
|
||||
file=$(realpath --no-symlinks "$file")
|
||||
if [ -z "$file" ] || grep -q -x -F "$file" <<< "$forbidden_paths"; then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Remove a file or a directory securely
|
||||
#
|
||||
# usage: ynh_secure_remove --file=path_to_remove
|
||||
|
@ -714,28 +736,18 @@ ynh_secure_remove() {
|
|||
ynh_handle_getopts_args "$@"
|
||||
set +o xtrace # set +x
|
||||
|
||||
local forbidden_path=" \
|
||||
/var/www \
|
||||
/home/yunohost.app"
|
||||
|
||||
if [ $# -ge 2 ]; then
|
||||
ynh_print_warn --message="/!\ Packager ! You provided more than one argument to ynh_secure_remove but it will be ignored... Use this helper with one argument at time."
|
||||
fi
|
||||
|
||||
if [[ -z "$file" ]]; then
|
||||
ynh_print_warn --message="ynh_secure_remove called with empty argument, ignoring."
|
||||
elif [[ "$forbidden_path" =~ "$file" ||
|
||||
|
||||
"$file" =~ ^/[[:alnum:]]+$ ||
|
||||
|
||||
"${file:${#file}-1}" = "/" ]]; then # Match all paths or subpaths in $forbidden_path
|
||||
# Match all first level paths from / (Like /var, /root, etc...)
|
||||
# Match if the path finishes by /. Because it seems there is an empty variable
|
||||
ynh_print_warn --message="Not deleting '$file' because it is not an acceptable path to delete."
|
||||
elif [ -e "$file" ]; then
|
||||
rm --recursive "$file"
|
||||
else
|
||||
elif [[ ! -e $file ]]; then
|
||||
ynh_print_info --message="'$file' wasn't deleted because it doesn't exist."
|
||||
elif ! _acceptable_path_to_delete "$file"; then
|
||||
ynh_print_warn --message="Not deleting '$file' because it is not an acceptable path to delete."
|
||||
else
|
||||
rm --recursive "$file"
|
||||
fi
|
||||
|
||||
set -o xtrace # set -x
|
||||
|
|
|
@ -32,6 +32,7 @@ do_pre_regen() {
|
|||
|
||||
# add domain conf files
|
||||
for domain in $YNH_DOMAINS; do
|
||||
[[ ! $domain =~ \.local$ ]] || continue
|
||||
export domain
|
||||
ynh_render_template "domain.tpl" "${dnsmasq_dir}/${domain}"
|
||||
done
|
||||
|
@ -40,8 +41,10 @@ do_pre_regen() {
|
|||
conf_files=$(ls -1 /etc/dnsmasq.d \
|
||||
| awk '/^[^\.]+\.[^\.]+.*$/ { print $1 }')
|
||||
for domain in $conf_files; do
|
||||
[[ $YNH_DOMAINS =~ $domain ]] \
|
||||
|| touch "${dnsmasq_dir}/${domain}"
|
||||
if [[ ! $YNH_DOMAINS =~ $domain ]] && [[ ! $domain =~ \.local$ ]]
|
||||
then
|
||||
touch "${dnsmasq_dir}/${domain}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
|
|
|
@ -78,6 +78,20 @@ service quota-warning {
|
|||
}
|
||||
}
|
||||
|
||||
service stats {
|
||||
unix_listener stats-reader {
|
||||
user = vmail
|
||||
group = mail
|
||||
mode = 0660
|
||||
}
|
||||
|
||||
unix_listener stats-writer {
|
||||
user = vmail
|
||||
group = mail
|
||||
mode = 0660
|
||||
}
|
||||
}
|
||||
|
||||
plugin {
|
||||
sieve = /var/mail/sievescript/%n/.dovecot.sieve
|
||||
sieve_dir = /var/mail/sievescript/%n/scripts/
|
||||
|
|
20
debian/changelog
vendored
20
debian/changelog
vendored
|
@ -1,3 +1,23 @@
|
|||
yunohost (4.3.1.3) testing; urgency=low
|
||||
|
||||
- [fix] app: repo url branch names may contain dots (38cff4a9)
|
||||
|
||||
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 07 Oct 2021 18:31:09 +0200
|
||||
|
||||
yunohost (4.3.1.2) testing; urgency=low
|
||||
|
||||
- [fix] apps: upgrade was broken because of typo ([#1350](https://github.com/YunoHost/yunohost/pull/1350))
|
||||
- [enh] apps: in app_info, return a new is_webapp info meant to be used by API/webadmin (4cd5e9b6)
|
||||
- [fix] configpanel: handle case where file question didnt get modified from webadmin, in which case self.value contains a path (54d901ad)
|
||||
- [fix] configpanel: bind_key -> bind_key_ to prevent yunohost from redacting key names which leads to broken log metadata.yml somehow (941cc294)
|
||||
- [enh] questions: Add visible attribute support in cli (74256845)
|
||||
- [enh] helpers: Simplify apt/php dependencies helpers ([#1018](https://github.com/YunoHost/yunohost/pull/1018))
|
||||
- [enh] helpers: In logrotate helper, enforce decent permissions on log file if app user exists ([#1352](https://github.com/YunoHost/yunohost/pull/1352))
|
||||
|
||||
Thanks to all contributors <3 ! (Éric Gaspar, Kay0u, ljf)
|
||||
|
||||
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 07 Oct 2021 10:42:06 +0200
|
||||
|
||||
yunohost (4.3.1.1) testing; urgency=low
|
||||
|
||||
- [enh] app helpers: Update n version ([#1347](https://github.com/YunoHost/yunohost/pull/1347))
|
||||
|
|
|
@ -79,7 +79,7 @@ re_app_instance_name = re.compile(
|
|||
)
|
||||
|
||||
APP_REPO_URL = re.compile(
|
||||
r"^https://[a-zA-Z0-9-_.]+/[a-zA-Z0-9-_./]+/[a-zA-Z0-9-_.]+_ynh(/?(-/)?tree/[a-zA-Z0-9-_]+)?(\.git)?/?$"
|
||||
r"^https://[a-zA-Z0-9-_.]+/[a-zA-Z0-9-_./]+/[a-zA-Z0-9-_.]+_ynh(/?(-/)?tree/[a-zA-Z0-9-_.]+)?(\.git)?/?$"
|
||||
)
|
||||
|
||||
APP_FILES_TO_COPY = [
|
||||
|
@ -169,7 +169,7 @@ def app_info(app, full=False):
|
|||
ret["from_catalog"] = _load_apps_catalog()["apps"].get(absolute_app_name, {})
|
||||
ret["upgradable"] = _app_upgradable(ret)
|
||||
|
||||
ret["is_webapp"] = ("domain" in settings and "path" in settings)
|
||||
ret["is_webapp"] = "domain" in settings and "path" in settings
|
||||
|
||||
ret["supports_change_url"] = os.path.exists(
|
||||
os.path.join(setting_path, "scripts", "change_url")
|
||||
|
|
|
@ -68,6 +68,7 @@ def test_repo_url_definition():
|
|||
assert _is_app_repo_url(
|
||||
"https://gitlab.domainepublic.net/Neutrinet/neutrinet_ynh/-/tree/unstable"
|
||||
)
|
||||
assert _is_app_repo_url("https://github.com/YunoHost-Apps/foobar_ynh/tree/1.23.4")
|
||||
assert _is_app_repo_url("git@github.com:YunoHost-Apps/foobar_ynh.git")
|
||||
|
||||
assert not _is_app_repo_url("github.com/YunoHost-Apps/foobar_ynh")
|
||||
|
|
|
@ -15,7 +15,7 @@ from yunohost.utils.config import (
|
|||
PathQuestion,
|
||||
BooleanQuestion,
|
||||
FileQuestion,
|
||||
evaluate_simple_js_expression
|
||||
evaluate_simple_js_expression,
|
||||
)
|
||||
from yunohost.utils.error import YunohostError, YunohostValidationError
|
||||
|
||||
|
@ -2095,97 +2095,95 @@ def test_normalize_path():
|
|||
assert PathQuestion.normalize("macnuggets/") == "/macnuggets"
|
||||
assert PathQuestion.normalize("////macnuggets///") == "/macnuggets"
|
||||
|
||||
|
||||
def test_simple_evaluate():
|
||||
context = {
|
||||
'a1': 1,
|
||||
'b2': 2,
|
||||
'c10': 10,
|
||||
'foo': 'bar',
|
||||
'comp': '1>2',
|
||||
'empty': '',
|
||||
'lorem': 'Lorem ipsum dolor et si qua met!',
|
||||
'warning': 'Warning! This sentence will fail!',
|
||||
'quote': "Je s'apelle Groot",
|
||||
'and_': '&&',
|
||||
'object': { 'a': 'Security risk' }
|
||||
"a1": 1,
|
||||
"b2": 2,
|
||||
"c10": 10,
|
||||
"foo": "bar",
|
||||
"comp": "1>2",
|
||||
"empty": "",
|
||||
"lorem": "Lorem ipsum dolor et si qua met!",
|
||||
"warning": "Warning! This sentence will fail!",
|
||||
"quote": "Je s'apelle Groot",
|
||||
"and_": "&&",
|
||||
"object": {"a": "Security risk"},
|
||||
}
|
||||
supported = {
|
||||
'42': 42,
|
||||
'9.5': 9.5,
|
||||
"'bopbidibopbopbop'": 'bopbidibopbopbop',
|
||||
'true': True,
|
||||
'false': False,
|
||||
'null': None,
|
||||
|
||||
"42": 42,
|
||||
"9.5": 9.5,
|
||||
"'bopbidibopbopbop'": "bopbidibopbopbop",
|
||||
"true": True,
|
||||
"false": False,
|
||||
"null": None,
|
||||
# Math
|
||||
'1 * (2 + 3 * (4 - 3))': 5,
|
||||
'1 * (2 + 3 * (4 - 3)) > 10 - 2 || 3 * 2 > 9 - 2 * 3': True,
|
||||
'(9 - 2) * 3 - 10': 11,
|
||||
'12 - 2 * -2 + (3 - 4) * 3.1': 12.9,
|
||||
'9 / 12 + 12 * 3 - 5': 31.75,
|
||||
'9 / 12 + 12 * (3 - 5)': -23.25,
|
||||
'12 > 13.1': False,
|
||||
'12 < 14': True,
|
||||
'12 <= 14': True,
|
||||
'12 >= 14': False,
|
||||
'12 == 14': False,
|
||||
'12 % 5 > 3': False,
|
||||
'12 != 14': True,
|
||||
'9 - 1 > 10 && 3 * 5 > 10': False,
|
||||
'9 - 1 > 10 || 3 * 5 > 10': True,
|
||||
'a1 > 0 || a1 < -12': True,
|
||||
'a1 > 0 && a1 < -12': False,
|
||||
'a1 + 1 > 0 && -a1 > -12': True,
|
||||
'-(a1 + 1) < 0 || -(a1 + 2) > -12': True,
|
||||
'-a1 * 2': -2,
|
||||
'(9 - 2) * 3 - c10': 11,
|
||||
'(9 - b2) * 3 - c10': 11,
|
||||
'c10 > b2': True,
|
||||
|
||||
"1 * (2 + 3 * (4 - 3))": 5,
|
||||
"1 * (2 + 3 * (4 - 3)) > 10 - 2 || 3 * 2 > 9 - 2 * 3": True,
|
||||
"(9 - 2) * 3 - 10": 11,
|
||||
"12 - 2 * -2 + (3 - 4) * 3.1": 12.9,
|
||||
"9 / 12 + 12 * 3 - 5": 31.75,
|
||||
"9 / 12 + 12 * (3 - 5)": -23.25,
|
||||
"12 > 13.1": False,
|
||||
"12 < 14": True,
|
||||
"12 <= 14": True,
|
||||
"12 >= 14": False,
|
||||
"12 == 14": False,
|
||||
"12 % 5 > 3": False,
|
||||
"12 != 14": True,
|
||||
"9 - 1 > 10 && 3 * 5 > 10": False,
|
||||
"9 - 1 > 10 || 3 * 5 > 10": True,
|
||||
"a1 > 0 || a1 < -12": True,
|
||||
"a1 > 0 && a1 < -12": False,
|
||||
"a1 + 1 > 0 && -a1 > -12": True,
|
||||
"-(a1 + 1) < 0 || -(a1 + 2) > -12": True,
|
||||
"-a1 * 2": -2,
|
||||
"(9 - 2) * 3 - c10": 11,
|
||||
"(9 - b2) * 3 - c10": 11,
|
||||
"c10 > b2": True,
|
||||
# String
|
||||
"foo == 'bar'":True,
|
||||
"foo != 'bar'":False,
|
||||
'foo == "bar" && 1 > 0':True,
|
||||
'!!foo': True,
|
||||
'!foo': False,
|
||||
'foo': 'bar',
|
||||
"foo == 'bar'": True,
|
||||
"foo != 'bar'": False,
|
||||
'foo == "bar" && 1 > 0': True,
|
||||
"!!foo": True,
|
||||
"!foo": False,
|
||||
"foo": "bar",
|
||||
'!(foo > "baa") || 1 > 2': False,
|
||||
'!(foo > "baa") || 1 < 2': True,
|
||||
'empty == ""': True,
|
||||
'1 == "1"': True,
|
||||
'1.0 == "1"': True,
|
||||
'1 == "aaa"': False,
|
||||
"'I am ' + b2 + ' years'": 'I am 2 years',
|
||||
"'I am ' + b2 + ' years'": "I am 2 years",
|
||||
"quote == 'Je s\\'apelle Groot'": True,
|
||||
"lorem == 'Lorem ipsum dolor et si qua met!'": True,
|
||||
"and_ == '&&'": True,
|
||||
"warning == 'Warning! This sentence will fail!'": True,
|
||||
|
||||
# Match
|
||||
"match(lorem, '^Lorem [ia]psumE?')": bool,
|
||||
"match(foo, '^Lorem [ia]psumE?')": None,
|
||||
"match(lorem, '^Lorem [ia]psumE?') && 1 == 1": bool,
|
||||
|
||||
# No code
|
||||
"": False,
|
||||
" ": False,
|
||||
}
|
||||
trigger_errors = {
|
||||
"object.a": YunohostError, # Keep unsupported, for security reasons
|
||||
'a1 ** b2': YunohostError, # Keep unsupported, for security reasons
|
||||
'().__class__.__bases__[0].__subclasses__()': YunohostError, # Very dangerous code
|
||||
'a1 > 11 ? 1 : 0': SyntaxError,
|
||||
'c10 > b2 == false': YunohostError, # JS and Python doesn't do the same thing for this situation
|
||||
'c10 > b2 == true': YunohostError,
|
||||
"object.a": YunohostError, # Keep unsupported, for security reasons
|
||||
"a1 ** b2": YunohostError, # Keep unsupported, for security reasons
|
||||
"().__class__.__bases__[0].__subclasses__()": YunohostError, # Very dangerous code
|
||||
"a1 > 11 ? 1 : 0": SyntaxError,
|
||||
"c10 > b2 == false": YunohostError, # JS and Python doesn't do the same thing for this situation
|
||||
"c10 > b2 == true": YunohostError,
|
||||
}
|
||||
|
||||
for expression, result in supported.items():
|
||||
if result == bool:
|
||||
assert bool(evaluate_simple_js_expression(expression, context)), expression
|
||||
else:
|
||||
assert evaluate_simple_js_expression(expression, context) == result, expression
|
||||
assert (
|
||||
evaluate_simple_js_expression(expression, context) == result
|
||||
), expression
|
||||
|
||||
for expression, error in trigger_errors.items():
|
||||
with pytest.raises(error):
|
||||
evaluate_simple_js_expression(expression, context)
|
||||
|
||||
|
|
|
@ -55,24 +55,24 @@ def evaluate_simple_ast(node, context={}):
|
|||
operators = {
|
||||
ast.Not: op.not_,
|
||||
ast.Mult: op.mul,
|
||||
ast.Div: op.truediv, # number
|
||||
ast.Mod: op.mod, # number
|
||||
ast.Add: op.add, #str
|
||||
ast.Sub: op.sub, #number
|
||||
ast.USub: op.neg, # Negative number
|
||||
ast.Div: op.truediv, # number
|
||||
ast.Mod: op.mod, # number
|
||||
ast.Add: op.add, # str
|
||||
ast.Sub: op.sub, # number
|
||||
ast.USub: op.neg, # Negative number
|
||||
ast.Gt: op.gt,
|
||||
ast.Lt: op.lt,
|
||||
ast.GtE: op.ge,
|
||||
ast.LtE: op.le,
|
||||
ast.Eq: op.eq,
|
||||
ast.NotEq: op.ne
|
||||
ast.NotEq: op.ne,
|
||||
}
|
||||
context['true'] = True
|
||||
context['false'] = False
|
||||
context['null'] = None
|
||||
context["true"] = True
|
||||
context["false"] = False
|
||||
context["null"] = None
|
||||
|
||||
# Variable
|
||||
if isinstance(node, ast.Name): # Variable
|
||||
if isinstance(node, ast.Name): # Variable
|
||||
return context[node.id]
|
||||
|
||||
# Python <=3.7 String
|
||||
|
@ -88,14 +88,16 @@ def evaluate_simple_ast(node, context={}):
|
|||
return node.value
|
||||
|
||||
# + - * / %
|
||||
elif isinstance(node, ast.BinOp) and type(node.op) in operators: # <left> <operator> <right>
|
||||
elif (
|
||||
isinstance(node, ast.BinOp) and type(node.op) in operators
|
||||
): # <left> <operator> <right>
|
||||
left = evaluate_simple_ast(node.left, context)
|
||||
right = evaluate_simple_ast(node.right, context)
|
||||
if type(node.op) == ast.Add:
|
||||
if isinstance(left, str) or isinstance(right, str): # support 'I am ' + 42
|
||||
if isinstance(left, str) or isinstance(right, str): # support 'I am ' + 42
|
||||
left = str(left)
|
||||
right = str(right)
|
||||
elif type(left) != type(right): # support "111" - "1" -> 110
|
||||
elif type(left) != type(right): # support "111" - "1" -> 110
|
||||
left = float(left)
|
||||
right = float(right)
|
||||
|
||||
|
@ -104,7 +106,9 @@ def evaluate_simple_ast(node, context={}):
|
|||
# Comparison
|
||||
# JS and Python don't give the same result for multi operators
|
||||
# like True == 10 > 2.
|
||||
elif isinstance(node, ast.Compare) and len(node.comparators) == 1: # <left> <ops> <comparators>
|
||||
elif (
|
||||
isinstance(node, ast.Compare) and len(node.comparators) == 1
|
||||
): # <left> <ops> <comparators>
|
||||
left = evaluate_simple_ast(node.left, context)
|
||||
right = evaluate_simple_ast(node.comparators[0], context)
|
||||
operator = node.ops[0]
|
||||
|
@ -116,11 +120,11 @@ def evaluate_simple_ast(node, context={}):
|
|||
return type(operator) == ast.NotEq
|
||||
try:
|
||||
return operators[type(operator)](left, right)
|
||||
except TypeError: # support "e" > 1 -> False like in JS
|
||||
except TypeError: # support "e" > 1 -> False like in JS
|
||||
return False
|
||||
|
||||
# and / or
|
||||
elif isinstance(node, ast.BoolOp): # <op> <values>
|
||||
elif isinstance(node, ast.BoolOp): # <op> <values>
|
||||
for value in node.values:
|
||||
value = evaluate_simple_ast(value, context)
|
||||
if isinstance(node.op, ast.And) and not value:
|
||||
|
@ -130,20 +134,22 @@ def evaluate_simple_ast(node, context={}):
|
|||
return isinstance(node.op, ast.And)
|
||||
|
||||
# not / USub (it's negation number -\d)
|
||||
elif isinstance(node, ast.UnaryOp): # <operator> <operand> e.g., -1
|
||||
elif isinstance(node, ast.UnaryOp): # <operator> <operand> e.g., -1
|
||||
return operators[type(node.op)](evaluate_simple_ast(node.operand, context))
|
||||
|
||||
# match function call
|
||||
elif isinstance(node, ast.Call) and node.func.__dict__.get('id') == 'match':
|
||||
elif isinstance(node, ast.Call) and node.func.__dict__.get("id") == "match":
|
||||
return re.match(
|
||||
evaluate_simple_ast(node.args[1], context),
|
||||
context[node.args[0].id]
|
||||
evaluate_simple_ast(node.args[1], context), context[node.args[0].id]
|
||||
)
|
||||
|
||||
# Unauthorized opcode
|
||||
else:
|
||||
opcode = str(type(node))
|
||||
raise YunohostError(f"Unauthorize opcode '{opcode}' in visible attribute", raw_msg=True)
|
||||
raise YunohostError(
|
||||
f"Unauthorize opcode '{opcode}' in visible attribute", raw_msg=True
|
||||
)
|
||||
|
||||
|
||||
def js_to_python(expr):
|
||||
in_string = None
|
||||
|
@ -162,7 +168,7 @@ def js_to_python(expr):
|
|||
|
||||
# If we are not in a string, replace operators
|
||||
elif not in_string:
|
||||
if char == "!" and expr[i +1] != "=":
|
||||
if char == "!" and expr[i + 1] != "=":
|
||||
char = "not "
|
||||
elif char in "|&" and py_expr[-1:] == char:
|
||||
py_expr = py_expr[:-1]
|
||||
|
@ -171,15 +177,17 @@ def js_to_python(expr):
|
|||
# Determine if next loop will be in escaped mode
|
||||
escaped = char == "\\" and not escaped
|
||||
py_expr += char
|
||||
i+=1
|
||||
i += 1
|
||||
return py_expr
|
||||
|
||||
|
||||
def evaluate_simple_js_expression(expr, context={}):
|
||||
if not expr.strip():
|
||||
return False
|
||||
node = ast.parse(js_to_python(expr), mode="eval").body
|
||||
return evaluate_simple_ast(node, context)
|
||||
|
||||
|
||||
class ConfigPanel:
|
||||
def __init__(self, config_path, save_path=None):
|
||||
self.config_path = config_path
|
||||
|
@ -648,7 +656,9 @@ class Question(object):
|
|||
|
||||
def ask_if_needed(self):
|
||||
|
||||
if self.visible and not evaluate_simple_js_expression(self.visible, context=self.context):
|
||||
if self.visible and not evaluate_simple_js_expression(
|
||||
self.visible, context=self.context
|
||||
):
|
||||
# FIXME There could be several use case if the question is not displayed:
|
||||
# - we doesn't want to give a specific value
|
||||
# - we want to keep the previous value
|
||||
|
@ -1231,7 +1241,6 @@ def ask_questions_and_parse_answers(
|
|||
else:
|
||||
answers = {}
|
||||
|
||||
|
||||
out = []
|
||||
|
||||
for raw_question in raw_questions:
|
||||
|
|
71
tests/test_helpers.d/ynhtest_secure_remove.sh
Normal file
71
tests/test_helpers.d/ynhtest_secure_remove.sh
Normal file
|
@ -0,0 +1,71 @@
|
|||
ynhtest_acceptable_path_to_delete() {
|
||||
|
||||
mkdir -p /home/someuser
|
||||
mkdir -p /home/$app
|
||||
mkdir -p /home/yunohost.app/$app
|
||||
mkdir -p /var/www/$app
|
||||
touch /var/www/$app/bar
|
||||
touch /etc/cron.d/$app
|
||||
|
||||
! _acceptable_path_to_delete /
|
||||
! _acceptable_path_to_delete ////
|
||||
! _acceptable_path_to_delete " //// "
|
||||
! _acceptable_path_to_delete /var
|
||||
! _acceptable_path_to_delete /var/www
|
||||
! _acceptable_path_to_delete /var/cache
|
||||
! _acceptable_path_to_delete /usr
|
||||
! _acceptable_path_to_delete /usr/bin
|
||||
! _acceptable_path_to_delete /home
|
||||
! _acceptable_path_to_delete /home/yunohost.backup
|
||||
! _acceptable_path_to_delete /home/yunohost.app
|
||||
! _acceptable_path_to_delete /home/yunohost.app/
|
||||
! _acceptable_path_to_delete ///home///yunohost.app///
|
||||
! _acceptable_path_to_delete /home/yunohost.app/$app/..
|
||||
! _acceptable_path_to_delete ///home///yunohost.app///$app///..//
|
||||
! _acceptable_path_to_delete /home/yunohost.app/../$app/..
|
||||
! _acceptable_path_to_delete /home/someuser
|
||||
! _acceptable_path_to_delete /home/yunohost.app//../../$app
|
||||
! _acceptable_path_to_delete " /home/yunohost.app/// "
|
||||
! _acceptable_path_to_delete /etc/cron.d/
|
||||
! _acceptable_path_to_delete /etc/yunohost/
|
||||
|
||||
_acceptable_path_to_delete /home/yunohost.app/$app
|
||||
_acceptable_path_to_delete /home/yunohost.app/$app/bar
|
||||
_acceptable_path_to_delete /etc/cron.d/$app
|
||||
_acceptable_path_to_delete /var/www/$app/bar
|
||||
_acceptable_path_to_delete /var/www/$app
|
||||
|
||||
rm /var/www/$app/bar
|
||||
rm /etc/cron.d/$app
|
||||
rmdir /home/yunohost.app/$app
|
||||
rmdir /home/$app
|
||||
rmdir /home/someuser
|
||||
rmdir /var/www/$app
|
||||
}
|
||||
|
||||
ynhtest_secure_remove() {
|
||||
|
||||
mkdir -p /home/someuser
|
||||
mkdir -p /home/yunohost.app/$app
|
||||
mkdir -p /var/www/$app
|
||||
mkdir -p /var/whatever
|
||||
touch /var/www/$app/bar
|
||||
touch /etc/cron.d/$app
|
||||
|
||||
! ynh_secure_remove --file="/home/someuser"
|
||||
! ynh_secure_remove --file="/home/yunohost.app/"
|
||||
! ynh_secure_remove --file="/var/whatever"
|
||||
ynh_secure_remove --file="/home/yunohost.app/$app"
|
||||
ynh_secure_remove --file="/var/www/$app"
|
||||
ynh_secure_remove --file="/etc/cron.d/$app"
|
||||
|
||||
test -e /home/someuser
|
||||
test -e /home/yunohost.app
|
||||
test -e /var/whatever
|
||||
! test -e /home/yunohost.app/$app
|
||||
! test -e /var/www/$app
|
||||
! test -e /etc/cron.d/$app
|
||||
|
||||
rmdir /home/someuser
|
||||
rmdir /var/whatever
|
||||
}
|
Loading…
Add table
Reference in a new issue