From e6b606e08c78dcd8bd48d846541f4e5344a44e96 Mon Sep 17 00:00:00 2001 From: Matt Bovel <matthieu@bovel.net> Date: Fri, 5 Jul 2024 15:41:00 +0200 Subject: [PATCH] Run student's code as a different user, add timeout --- test-images/demo-python3/Dockerfile | 10 ++++++ test-images/demo-python3/README.md | 3 +- test-images/demo-python3/grade.sh | 31 ++++++++++++------- .../test_submissions/malicious.py | 13 ++++++++ 4 files changed, 45 insertions(+), 12 deletions(-) create mode 100755 test-images/demo-python3/test_submissions/malicious.py diff --git a/test-images/demo-python3/Dockerfile b/test-images/demo-python3/Dockerfile index 89033a2c..091406d8 100644 --- a/test-images/demo-python3/Dockerfile +++ b/test-images/demo-python3/Dockerfile @@ -1,5 +1,15 @@ FROM python:3.13.0b3-alpine3.20 + +# Create a less-privileged user to run student's code and install sudo +RUN addgroup -S student && adduser -S student -G student +RUN apk add --no-cache --update sudo + WORKDIR /usr/src/app COPY ./grade.sh grade.sh RUN chmod +x grade.sh + +# `/data` is the directory where the `submission` and `feedback` directories are +# mounted. Make sure that only root can access these directories. +RUN mkdir -p /data && chmod 700 -R /data + CMD ["./grade.sh"] diff --git a/test-images/demo-python3/README.md b/test-images/demo-python3/README.md index d85e6996..5f815827 100644 --- a/test-images/demo-python3/README.md +++ b/test-images/demo-python3/README.md @@ -19,8 +19,9 @@ After building the image you can try out the grading image locally, by mounting Example submissions are provided in `./test_submissions`. For instance, to grade the correct submission `/test_submissions/correct.py` using the image, run the following command: ```bash +rm -rf feedback mkdir feedback -docker run -v ./feedback:/data/feedback -v ./test_submissions/correct.py:/data/submission/script.py demo-python3 +docker run --rm -v ./feedback:/data/feedback -v ./test_submissions/correct.py:/data/submission/script.py demo-python3 cat feedback/grade.json ``` diff --git a/test-images/demo-python3/grade.sh b/test-images/demo-python3/grade.sh index c9e792a7..545620c1 100644 --- a/test-images/demo-python3/grade.sh +++ b/test-images/demo-python3/grade.sh @@ -6,29 +6,38 @@ save_feedback() { # Feedback must be saved in `/data/feedback`. # - `grade.json`, must be a JSON file with {"grade": grade_as_a_number} # - other file are attached as feedback files + echo "[*] Saving grade $grade and feedback \"$feedback\"" mkdir -p /data/feedback echo "{\"grade\": $grade}" > /data/feedback/grade.json echo "$feedback" > /data/feedback/feedback.txt + exit 0 } grade() { # Files submitted by the student are mounted in `/data/submission`. - if [ -f /data/submission/script.py ]; then - output=$(python3 /data/submission/script.py) - echo "[*] Python output: $output" - if [ "Hello World!" == "$output" ]; then - save_feedback 100 "The output is correct, well done! :)" - else - save_feedback 0 "The output is not correct :'(" - fi - else + + # Check that the expected file is present + if [ ! -f /data/submission/script.py ]; then save_feedback 0 "I have not found the script.py file :'(" fi + + # Run the script under the restricted `student` user with a timeout of 1 + # minute, and capture the output. + cp /data/submission/script.py /home/student/script.py + chown student:student /home/student/script.py + output=$(timeout 1m sudo -u student python3 /home/student/script.py 2>&1) + + echo "[*] Python output: $output" + if [ "$output" == "Hello World!" ]; then + save_feedback 100 "The output is correct, well done! :)" + else + save_feedback 0 "The output is not correct :'(" + fi } handle_internal_error() { - save_feedback 0 "An internal error occurred, please contact your teacher." + save_feedback 0 "An internal error occurred, please contact the teaching team." } -# Important: always exit with 0 +# Important: make sure to always exit with 0 grade || handle_internal_error diff --git a/test-images/demo-python3/test_submissions/malicious.py b/test-images/demo-python3/test_submissions/malicious.py new file mode 100755 index 00000000..3951052e --- /dev/null +++ b/test-images/demo-python3/test_submissions/malicious.py @@ -0,0 +1,13 @@ +import os +import json + +os.system("ls -l /data/submission") +os.system("whoami") + +os.makedirs('/data/feedback', exist_ok=True) + +with open("/data/feedback/grade.json", "w") as f: + json.dump({"grade": 100}, f) + +with open("/data/feedback/ahah.txt", "w") as f: + f.write("Ahah") -- GitLab