Claudio Salazar
import docker
client = docker.from_env()
input_including_plugin_code = ...
container = client.containers.run(
"my-image",
input_including_plugin_code,
detach=True,
)
...
container = client.containers.run(
"my-image",
input_including_plugin_code,
detach=True,
network_disabled=True,
)
Prevent access to network resources
...
container = client.containers.run(
"my-image",
input_including_plugin_code,
detach=True,
network_disabled=True,
nano_cpus=25 * 10**7,
mem_limit="128m",
)
Prevent exhausting system resources
import requests
...
MAX_SECONDS_TO_RUN = 10
try:
container.wait(timeout=MAX_SECONDS_TO_RUN)
except requests.exceptions.ConnectionError:
container.stop(timeout=0)
Prevent to run forever (DoS)
import requests
...
MAX_SECONDS_TO_RUN = 10
try:
container.wait(timeout=MAX_SECONDS_TO_RUN)
except requests.exceptions.ConnectionError:
container.stop(timeout=0)
# process container output
...
container.remove()
Prevent side-effects of the solution
from django.contrib.auth import get_user_model
from django.db import models
class Order(models.Model):
price = models.IntegerField()
user = models.ForeignKey(
get_user_model(),
on_delete=models.CASCADE
)
secret_code = models.CharField(max_length=20)
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
]
}
from rest_framework import serializers, viewsets
from .models import Order
class OrderSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ["id", "price"]
class OrderViewSet(viewsets.ReadOnlyModelViewSet):
def get_queryset(self):
return Order.objects.filter(user=self.request.user)
serializer_class = OrderSerializer
def test_reject_anonymous_request(api_client):
orders_url = reverse("orders-list")
res = api_client(anon=True).get(orders_url)
assert res.status_code == status.HTTP_403_FORBIDDEN
Test the defaults that you rely on
diff --git a/settings.py b/settings.py
index d41ee09..0aa5d51 100644
--- a/settings.py
+++ b/settings.py
@@ -1,5 +1,5 @@
REST_FRAMEWORK = {
"DEFAULT_PERMISSION_CLASSES": [
- "rest_framework.permissions.IsAuthenticated",
+ "rest_framework.permissions.AllowAny",
]
}
A new decision changes the default policy
def test_secret_code_is_not_returned(api_client):
user = UserFactory.create()
order = OrderFactory.create(user=user)
orders_url = reverse("orders-detail", args=(order.id,))
res = api_client(user).get(orders_url)
assert "secret_code" not in res.json()
Test you're not exposing sensitive data
diff --git a/demo/orders/views.py b/demo/orders/views.py
index f42cec0..38e473d 100644
--- a/demo/orders/views.py
+++ b/demo/orders/views.py
@@ -6,7 +6,7 @@ from .models import Order
class OrderSerializer(serializers.ModelSerializer):
class Meta:
model = Order
- fields = ["id", "price"]
+ fields = "__all__"
There are more fields to expose
def test_only_list_retrieve(api_client):
user = UserFactory.create()
orders_url = reverse("orders-list")
res = api_client(user).options(orders_url)
assert res._headers["allow"][1] == "GET, HEAD, OPTIONS"
Test you only expose desired methods
diff --git a/demo/orders/views.py b/demo/orders/views.py
index f42cec0..a4b9da0 100644
--- a/demo/orders/views.py
+++ b/demo/orders/views.py
@@ -9,7 +9,7 @@ class OrderSerializer(serializers.ModelSerializer):
fields = ["id", "price"]
-class OrderViewSet(viewsets.ReadOnlyModelViewSet):
+class OrderViewSet(viewsets.ModelViewSet):
def get_queryset(self):
return Order.objects.filter(user=self.request.user)
Let's get ModelViewSet for every endpoint
def test_user_cant_access_another_user_order(api_client):
user_1 = UserFactory.create()
user_2 = UserFactory.create()
order = OrderFactory.create(user=user_1)
orders_url = reverse("orders-detail", args=(order.id,))
res = api_client(user_2).get(orders_url)
assert res.status_code == status.HTTP_404_NOT_FOUND
Test that everything works as expected
@pytest.mark.parametrize(
"username,authenticated,status_code",
[
("anon", False, 403),
("user_1", True, 404),
("user_from_other_group", True, 404),
...,
],
)
def test_access_to_resource(username, authenticated, status_code):
user = get_user(username, authenticated)
res = api_client(user).get("/endpoint")
assert res.status_code == status_code