sharing/bday/age_max: input validation
This commit is contained in:
@@ -609,7 +609,7 @@ DEFAULT_CONFIG_SCHEMA: types.CONFIG_SCHEMA = OrderedDict([
|
|||||||
("conversion_bday_age_max", {
|
("conversion_bday_age_max", {
|
||||||
"value": "99",
|
"value": "99",
|
||||||
"help": "conversion bday age max",
|
"help": "conversion bday age max",
|
||||||
"type": positive_int}),
|
"type": sharing.check_bday_max_age}),
|
||||||
])),
|
])),
|
||||||
("hook", OrderedDict([
|
("hook", OrderedDict([
|
||||||
("type", {
|
("type", {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import uuid
|
|||||||
from csv import DictWriter
|
from csv import DictWriter
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from http import client
|
from http import client
|
||||||
from typing import Sequence, Union
|
from typing import Any, Sequence, Union
|
||||||
from urllib.parse import parse_qs
|
from urllib.parse import parse_qs
|
||||||
|
|
||||||
from radicale import (config, httputils, pathutils, rights, storage, types,
|
from radicale import (config, httputils, pathutils, rights, storage, types,
|
||||||
@@ -123,14 +123,24 @@ TOKEN_PATTERN_V1: str = "v1/[a-zA-Z0-9_\\-]{44}"
|
|||||||
|
|
||||||
OVERLAY_PROPERTIES_WHITELIST: Sequence[str] = ("C:calendar-description", "ICAL:calendar-color", "CR:addressbook-description", "INF:addressbook-color", "D:displayname", "ICAL:calendar-order")
|
OVERLAY_PROPERTIES_WHITELIST: Sequence[str] = ("C:calendar-description", "ICAL:calendar-color", "CR:addressbook-description", "INF:addressbook-color", "D:displayname", "ICAL:calendar-order")
|
||||||
|
|
||||||
|
SHARING_BDAY_AGE_MAX: int = 199 # maximum age to prevent unexpected DoS by config
|
||||||
|
|
||||||
|
|
||||||
|
def check_bday_max_age(data: Any) -> int:
|
||||||
|
value = int(data)
|
||||||
|
if value < 0:
|
||||||
|
raise ValueError("value is negative: %d" % value)
|
||||||
|
if value > SHARING_BDAY_AGE_MAX:
|
||||||
|
raise ValueError("value exceeds maximum (%d): %d" % (SHARING_BDAY_AGE_MAX, value))
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
ACTIONS_WHITELIST: dict = {
|
ACTIONS_WHITELIST: dict = {
|
||||||
'template': {
|
'config': {
|
||||||
'conversion_bday_summary_template': str,
|
'conversion_bday_summary_template': str,
|
||||||
'conversion_bday_description_template': str,
|
'conversion_bday_description_template': str,
|
||||||
'conversion_bday_alarm_trigger_template': str,
|
'conversion_bday_alarm_trigger_template': str,
|
||||||
},
|
'conversion_bday_age_max': check_bday_max_age,
|
||||||
'limit': {
|
|
||||||
'conversion_bday_age_max': "positive_int",
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -862,9 +872,12 @@ class BaseSharing:
|
|||||||
if level1 in ACTIONS_WHITELIST:
|
if level1 in ACTIONS_WHITELIST:
|
||||||
for level2 in request_data['Actions'][level1]:
|
for level2 in request_data['Actions'][level1]:
|
||||||
if level2 in ACTIONS_WHITELIST[level1]:
|
if level2 in ACTIONS_WHITELIST[level1]:
|
||||||
if ACTIONS_WHITELIST[level1][level2] == "positive_int":
|
logger.trace(api_info + ": Actions validation: type='%r'", type(ACTIONS_WHITELIST[level1][level2]))
|
||||||
if int(request_data['Actions'][level1][level2]) < 0:
|
if callable(ACTIONS_WHITELIST[level1][level2]):
|
||||||
hint = "'" + level1 + "': {'" + level2 + "'} is negative"
|
try:
|
||||||
|
value = ACTIONS_WHITELIST[level1][level2](request_data['Actions'][level1][level2])
|
||||||
|
except ValueError:
|
||||||
|
hint = "'" + level1 + "': {'" + level2 + "'} is out-of-range"
|
||||||
valid = False
|
valid = False
|
||||||
break
|
break
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -5131,6 +5131,26 @@ permissions: RrWw""")
|
|||||||
"conversion_bday_description_template": "BDAY={year}-{month}-{day}",
|
"conversion_bday_description_template": "BDAY={year}-{month}-{day}",
|
||||||
}})
|
}})
|
||||||
|
|
||||||
|
logging.info("\n*** configuration test: conversion_bday_age_max < 0")
|
||||||
|
try:
|
||||||
|
self.configure({"sharing": {
|
||||||
|
"conversion_bday_age_max": -1,
|
||||||
|
}})
|
||||||
|
except RuntimeError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
logging.info("\n*** configuration test: conversion_bday_age_max > MAX")
|
||||||
|
try:
|
||||||
|
self.configure({"sharing": {
|
||||||
|
"conversion_bday_age_max": 200,
|
||||||
|
}})
|
||||||
|
except RuntimeError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
# verify content as user
|
# verify content as user
|
||||||
logging.info("\n*** GET collection user format:default -> ok")
|
logging.info("\n*** GET collection user format:default -> ok")
|
||||||
_, headers, answer = self.request("GET", path_shared_2, login="user:userpw")
|
_, headers, answer = self.request("GET", path_shared_2, login="user:userpw")
|
||||||
@@ -5217,11 +5237,44 @@ permissions: RrWw""")
|
|||||||
json_dict['User'] = "user"
|
json_dict['User'] = "user"
|
||||||
json_dict['PathMapped'] = path_mapped
|
json_dict['PathMapped'] = path_mapped
|
||||||
json_dict['PathOrToken'] = path_shared_r
|
json_dict['PathOrToken'] = path_shared_r
|
||||||
json_dict['Actions'] = {"limit": {
|
json_dict['Actions'] = {"config": {
|
||||||
"conversion_bday_age_max": -1,
|
"conversion_bday_age_max": -1,
|
||||||
}}
|
}}
|
||||||
_, headers, answer = self._sharing_api_json("map", "update", check=400, login="owner:ownerpw", json_dict=json_dict)
|
_, headers, answer = self._sharing_api_json("map", "update", check=400, login="owner:ownerpw", json_dict=json_dict)
|
||||||
|
|
||||||
|
# update template with invalid data test
|
||||||
|
logging.info("\n*** update map(bday) user/owner:r -> age_max exceeds MAX")
|
||||||
|
json_dict = {}
|
||||||
|
json_dict['User'] = "user"
|
||||||
|
json_dict['PathMapped'] = path_mapped
|
||||||
|
json_dict['PathOrToken'] = path_shared_r
|
||||||
|
json_dict['Actions'] = {"config": {
|
||||||
|
"conversion_bday_age_max": (sharing.SHARING_BDAY_AGE_MAX + 1),
|
||||||
|
}}
|
||||||
|
_, headers, answer = self._sharing_api_json("map", "update", check=400, login="owner:ownerpw", json_dict=json_dict)
|
||||||
|
|
||||||
|
# update template with valid data test
|
||||||
|
logging.info("\n*** update map(bday) user/owner:r -> age_max ok")
|
||||||
|
json_dict = {}
|
||||||
|
json_dict['User'] = "user"
|
||||||
|
json_dict['PathMapped'] = path_mapped
|
||||||
|
json_dict['PathOrToken'] = path_shared_r
|
||||||
|
json_dict['Actions'] = {"config": {
|
||||||
|
"conversion_bday_age_max": 0,
|
||||||
|
}}
|
||||||
|
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="owner:ownerpw", json_dict=json_dict)
|
||||||
|
|
||||||
|
# update template with valid data test
|
||||||
|
logging.info("\n*** update map(bday) user/owner:r -> age_max < MAX")
|
||||||
|
json_dict = {}
|
||||||
|
json_dict['User'] = "user"
|
||||||
|
json_dict['PathMapped'] = path_mapped
|
||||||
|
json_dict['PathOrToken'] = path_shared_r
|
||||||
|
json_dict['Actions'] = {"config": {
|
||||||
|
"conversion_bday_age_max": sharing.SHARING_BDAY_AGE_MAX,
|
||||||
|
}}
|
||||||
|
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="owner:ownerpw", json_dict=json_dict)
|
||||||
|
|
||||||
logging.info("\n*** update map(bday) user/owner:r -> unsupported level 1")
|
logging.info("\n*** update map(bday) user/owner:r -> unsupported level 1")
|
||||||
json_dict = {}
|
json_dict = {}
|
||||||
json_dict['User'] = "user"
|
json_dict['User'] = "user"
|
||||||
|
|||||||
Reference in New Issue
Block a user