Skip to content
Permalink
Browse files Browse the repository at this point in the history
added url validation to all server requests
  • Loading branch information
vabene1111 committed May 17, 2022
1 parent 7fd5fca commit d48fe26
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 31 deletions.
7 changes: 5 additions & 2 deletions cookbook/integration/cookbookapp.py
Expand Up @@ -6,6 +6,7 @@
from io import BytesIO

import requests
import validators
import yaml

from cookbook.helper.ingredient_parser import IngredientParser
Expand Down Expand Up @@ -59,8 +60,10 @@ def get_recipe_from_file(self, file):

if len(images) > 0:
try:
response = requests.get(images[0])
self.import_recipe_image(recipe, BytesIO(response.content))
url = images[0]
if validators.url(url, public=True):
response = requests.get(url)
self.import_recipe_image(recipe, BytesIO(response.content))
except Exception as e:
print('failed to import image ', str(e))

Expand Down
5 changes: 4 additions & 1 deletion cookbook/integration/cookmate.py
Expand Up @@ -5,6 +5,7 @@
from gettext import gettext as _

import requests
import validators
from lxml import etree

from cookbook.helper.ingredient_parser import IngredientParser
Expand Down Expand Up @@ -64,7 +65,9 @@ def get_recipe_from_file(self, file):

if recipe_xml.find('imageurl') is not None:
try:
response = requests.get(recipe_xml.find('imageurl').text.strip())
url = recipe_xml.find('imageurl').text.strip()
if validators.url(url, public=True):
response = requests.get(url)
self.import_recipe_image(recipe, BytesIO(response.content))
except Exception as e:
print('failed to import image ', str(e))
Expand Down
13 changes: 8 additions & 5 deletions cookbook/integration/recettetek.py
Expand Up @@ -5,6 +5,7 @@
from zipfile import ZipFile

import requests
import validators

from django.utils.translation import gettext as _
from cookbook.helper.image_processing import get_filetype
Expand Down Expand Up @@ -123,11 +124,13 @@ def get_recipe_from_file(self, file):
self.import_recipe_image(recipe, BytesIO(import_zip.read(image_file_name)), filetype=get_filetype(image_file_name))
else:
if file['originalPicture'] != '':
response = requests.get(file['originalPicture'])
if imghdr.what(BytesIO(response.content)) is not None:
self.import_recipe_image(recipe, BytesIO(response.content), filetype=get_filetype(file['originalPicture']))
else:
raise Exception("Original image failed to download.")
url = file['originalPicture']
if validators.url(url, public=True):
response = requests.get(url)
if imghdr.what(BytesIO(response.content)) is not None:
self.import_recipe_image(recipe, BytesIO(response.content), filetype=get_filetype(file['originalPicture']))
else:
raise Exception("Original image failed to download.")
except Exception as e:
print(recipe.name, ': failed to import image ', str(e))

Expand Down
7 changes: 5 additions & 2 deletions cookbook/integration/recipesage.py
Expand Up @@ -2,6 +2,7 @@
from io import BytesIO

import requests
import validators

from cookbook.helper.ingredient_parser import IngredientParser
from cookbook.integration.integration import Integration
Expand Down Expand Up @@ -51,8 +52,10 @@ def get_recipe_from_file(self, file):

if len(file['image']) > 0:
try:
response = requests.get(file['image'][0])
self.import_recipe_image(recipe, BytesIO(response.content))
url = file['image'][0]
if validators.url(url, public=True):
response = requests.get(url)
self.import_recipe_image(recipe, BytesIO(response.content))
except Exception as e:
print('failed to import image ', str(e))

Expand Down
8 changes: 6 additions & 2 deletions cookbook/provider/dropbox.py
Expand Up @@ -4,6 +4,8 @@
from datetime import datetime

import requests
import validators

from cookbook.models import Recipe, RecipeImport, SyncLog
from cookbook.provider.provider import Provider

Expand Down Expand Up @@ -104,9 +106,11 @@ def get_file(recipe):
recipe.link = Dropbox.get_share_link(recipe)
recipe.save()

response = requests.get(recipe.link.replace('www.dropbox.', 'dl.dropboxusercontent.'))
url = recipe.link.replace('www.dropbox.', 'dl.dropboxusercontent.')
if validators.url(url, public=True):
response = requests.get(url)

return io.BytesIO(response.content)
return io.BytesIO(response.content)

@staticmethod
def rename_file(recipe, new_name):
Expand Down
24 changes: 13 additions & 11 deletions cookbook/provider/nextcloud.py
Expand Up @@ -4,6 +4,7 @@
from datetime import datetime

import requests
import validators
import webdav3.client as wc
from cookbook.models import Recipe, RecipeImport, SyncLog
from cookbook.provider.provider import Provider
Expand Down Expand Up @@ -92,20 +93,21 @@ def get_share_link(recipe):
"Content-Type": "application/json"
}

r = requests.get(
url,
headers=headers,
auth=HTTPBasicAuth(
recipe.storage.username, recipe.storage.password
if validators.url(url, public=True):
r = requests.get(
url,
headers=headers,
auth=HTTPBasicAuth(
recipe.storage.username, recipe.storage.password
)
)
)

response_json = r.json()
for element in response_json['ocs']['data']:
if element['share_type'] == '3':
return element['url']
response_json = r.json()
for element in response_json['ocs']['data']:
if element['share_type'] == '3':
return element['url']

return Nextcloud.create_share_link(recipe)
return Nextcloud.create_share_link(recipe)

@staticmethod
def get_file(recipe):
Expand Down
25 changes: 17 additions & 8 deletions cookbook/views/api.py
Expand Up @@ -6,6 +6,7 @@
from collections import OrderedDict

import requests
import validators
from PIL import UnidentifiedImageError
from annoying.decorators import ajax_request
from annoying.functions import get_object_or_None
Expand All @@ -14,7 +15,7 @@
from django.contrib.postgres.search import TrigramSimilarity
from django.core.exceptions import FieldError, ValidationError
from django.core.files import File
from django.db.models import (Case, Count, Exists, F, IntegerField, OuterRef, ProtectedError, Q,
from django.db.models import (Case, Count, Exists, OuterRef, ProtectedError, Q,
Subquery, Value, When)
from django.db.models.fields.related import ForeignObjectRel
from django.db.models.functions import Coalesce, Lower
Expand All @@ -24,7 +25,6 @@
from django.utils.translation import gettext as _
from django_scopes import scopes_disabled
from icalendar import Calendar, Event
from recipe_scrapers import NoSchemaFoundInWildMode, WebsiteNotImplementedError, scrape_me
from requests.exceptions import MissingSchema
from rest_framework import decorators, status, viewsets
from rest_framework.exceptions import APIException, PermissionDenied
Expand All @@ -34,6 +34,7 @@
from rest_framework.response import Response
from rest_framework.viewsets import ViewSetMixin
from treebeard.exceptions import InvalidMoveToDescendant, InvalidPosition, PathOverflow
from validators import ValidationFailure

from cookbook.helper.HelperFunctions import str2bool
from cookbook.helper.image_processing import handle_image
Expand All @@ -43,7 +44,6 @@
group_required)
from cookbook.helper.recipe_html_import import get_recipe_from_source
from cookbook.helper.recipe_search import RecipeFacet, RecipeSearch, old_search
from cookbook.helper.recipe_url_import import get_from_scraper
from cookbook.helper.shopping_helper import RecipeShoppingEditor, shopping_helper
from cookbook.models import (Automation, BookmarkletImport, CookLog, CustomFilter, ExportLog, Food,
FoodInheritField, ImportLog, Ingredient, Keyword, MealPlan, MealType,
Expand Down Expand Up @@ -774,16 +774,18 @@ def image(self, request, pk):
if serializer.is_valid():
serializer.save()
image = None
filetype = ".jpeg" # fall-back to .jpeg, even if wrong, at least users will know it's an image and most image viewers can open it correctly anyways
filetype = ".jpeg" # fall-back to .jpeg, even if wrong, at least users will know it's an image and most image viewers can open it correctly anyways

if 'image' in serializer.validated_data:
image = obj.image
filetype = mimetypes.guess_extension(serializer.validated_data['image'].content_type) or filetype
elif 'image_url' in serializer.validated_data:
try:
response = requests.get(serializer.validated_data['image_url'])
image = File(io.BytesIO(response.content))
filetype = mimetypes.guess_extension(response.headers['content-type']) or filetype
url = serializer.validated_data['image_url']
if validators.url(url, public=True):
response = requests.get(url)
image = File(io.BytesIO(response.content))
filetype = mimetypes.guess_extension(response.headers['content-type']) or filetype
except UnidentifiedImageError as e:
print(e)
pass
Expand Down Expand Up @@ -1188,7 +1190,13 @@ def recipe_from_source(request):
# in manual mode request complete page to return it later
if url:
try:
data = requests.get(url, headers=external_request_headers).content
if validators.url(url, public=True):
data = requests.get(url, headers=external_request_headers).content
else:
return JsonResponse({
'error': True,
'msg': _('Invalid Url')
}, status=400)
except requests.exceptions.ConnectionError:
return JsonResponse({
'error': True,
Expand All @@ -1199,6 +1207,7 @@ def recipe_from_source(request):
'error': True,
'msg': _('Bad URL Schema.')
}, status=400)

recipe_json, recipe_tree, recipe_html, recipe_images = get_recipe_from_source(data, url, request)
if len(recipe_tree) == 0 and len(recipe_json) == 0:
return JsonResponse({
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Expand Up @@ -43,3 +43,4 @@ python-ldap==3.4.0
django-auth-ldap==4.0.0
pytest-factoryboy==2.1.0
pyppeteer==1.0.2
validators==0.19.0

0 comments on commit d48fe26

Please sign in to comment.