diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 4eb0da3513..74349ebf61 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -36,7 +36,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -101,4 +101,4 @@ jobs: run: sudo chown -R ${USER} ./installdir - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/crunch42-analysis.yml b/.github/workflows/crunch42-analysis.yml new file mode 100644 index 0000000000..ac13fe8fab --- /dev/null +++ b/.github/workflows/crunch42-analysis.yml @@ -0,0 +1,42 @@ +name: "42Crunch REST API Static Security Testing" + +# follow standard Code Scanning triggers +on: + push: + branches: [ main ] + pull_request_target: + # The branches below must be a subset of the branches above + branches: [ main ] + schedule: + - cron: '19 4 * * 3' + +jobs: + rest-api-static-security-testing: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Install DOMjudge + run: .github/workflowscripts/baseinstall.sh + + - name: Dump the OpenAPI + run: .github/workflowscripts/getapi.sh + + - name: Find all other JSON files and delete those + run: | + rm -rf ./installdir/domserver/lib/vendor ./lib/vendor + rm -f ./doc/manual/sphinx-team.json ./doc/manual/sphinx-team.json + find ./ -name "*.json" + + - name: 42Crunch REST API Static Security Testing + uses: 42Crunch/api-security-audit-action@v1 + with: + # Follow these steps to configure API_SECRET https://docs.42crunch.com/latest/content/tasks/integrate_github_actions.htm + api-token: ${{ secrets.API_SECRET }} + min-score: 9 + # Upload results to Github code scanning + upload-to-code-scanning: true + # Github token for uploading the results + github-token: ${{ github.token }} + ignore-failures: false + diff --git a/.github/workflows/mayhem-api.yml b/.github/workflows/mayhem-api.yml index 6f4b8d7f36..a0246a285d 100644 --- a/.github/workflows/mayhem-api.yml +++ b/.github/workflows/mayhem-api.yml @@ -1,6 +1,7 @@ name: "Mayhem API analysis" on: + push: schedule: - cron: '5 21 * * *' @@ -22,20 +23,20 @@ jobs: - name: Install DOMjudge run: .github/workflowscripts/baseinstall.sh - - name: Dump the OpenAPI - run: .github/workflowscripts/getapi.sh + #- name: Dump the OpenAPI + # run: .github/workflowscripts/getapi.sh - - name: Mayhem for API - uses: ForAllSecure/mapi-action@193b709971cc377675e33284aecbf9229853e010 - continue-on-error: true - with: - mapi-token: ${{ secrets.MAPI_TOKEN }} - api-url: http://localhost/domjudge - api-spec: http://localhost/domjudge/api/doc.json # swagger/openAPI doc hosted here - duration: 60 - sarif-report: mapi.sarif + #- name: Mayhem for API + # uses: ForAllSecure/mapi-action@193b709971cc377675e33284aecbf9229853e010 + # continue-on-error: true + # with: + # mapi-token: ${{ secrets.MAPI_TOKEN }} + # api-url: http://localhost/domjudge + # api-spec: http://localhost/domjudge/api/doc.json # swagger/openAPI doc hosted here + # duration: 60 + # sarif-report: mapi.sarif - - name: Upload SARIF file - uses: github/codeql-action/upload-sarif@v1 - with: - sarif_file: mapi.sarif + #- name: Upload SARIF file + # uses: github/codeql-action/upload-sarif@v2 + # with: + # sarif_file: mapi.sarif diff --git a/.github/workflows/shiftleft.yml b/.github/workflows/shiftleft.yml index ed6df4d953..986856a307 100644 --- a/.github/workflows/shiftleft.yml +++ b/.github/workflows/shiftleft.yml @@ -1,32 +1,32 @@ -name: SL Scan - -on: - push: - branches: [ main ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ main ] - schedule: - - cron: '24 23 * * 6' - -jobs: - Scan-Build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - name: Perform Scan - uses: ShiftLeftSecurity/scan-action@master - env: - WORKSPACE: "" - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SCAN_AUTO_BUILD: true - SCAN_ANNOTATE_PR: 0 - with: - output: reports - type: python,bash - - - name: Upload report - uses: github/codeql-action/upload-sarif@v1 - with: - sarif_file: reports +name: SL Scan + +on: + push: + branches: [ main ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ main ] + schedule: + - cron: '24 23 * * 6' + +jobs: + Scan-Build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Perform Scan + uses: ShiftLeftSecurity/scan-action@master + env: + WORKSPACE: "" + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SCAN_AUTO_BUILD: true + SCAN_ANNOTATE_PR: 0 + with: + output: reports + type: python,bash + + - name: Upload report + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: reports diff --git a/.github/workflowscripts/baseinstall.sh b/.github/workflowscripts/baseinstall.sh index fb4865ba88..660054d5f0 100755 --- a/.github/workflowscripts/baseinstall.sh +++ b/.github/workflowscripts/baseinstall.sh @@ -1,8 +1,29 @@ #!/bin/sh +# Functions to annotate the Github actions logs +alias trace_on='set -x' +alias trace_off='{ set +x; } 2>/dev/null' + +section_start_internal () { + echo "::group::$1" + trace_on +} + +section_end_internal () { + echo "::endgroup::" + trace_on +} + +alias section_start='trace_off ; section_start_internal ' +alias section_end='trace_off ; section_end_internal ' + set -eux +section_start "Update packages" sudo apt update +section_end + +section_start "Install needed packages" sudo apt install -y acl zip unzip nginx php php-fpm php-gd \ php-cli php-intl php-mbstring php-mysql php-curl php-json \ php-xml php-zip ntp make sudo debootstrap \ @@ -11,35 +32,49 @@ sudo apt install -y acl zip unzip nginx php php-fpm php-gd \ default-jdk-headless ghc fp-compiler autoconf automake bats \ python3-sphinx python3-sphinx-rtd-theme rst2pdf fontconfig \ python3-yaml latexmk curl +section_end + +section_start "Install composer" php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" HASH="$(wget -q -O - https://composer.github.io/installer.sig)" php -r "if (hash_file('SHA384', 'composer-setup.php') === '$HASH') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" sudo php composer-setup.php --install-dir=/usr/local/bin --filename=composer +section_end +section_start "Run composer" +export APP_ENV="dev" composer install --no-scripts +section_end -sudo sed -i "s/\$operation->operationId =/#\$operation->operationId =/g" lib/vendor/nelmio/api-doc-bundle/OpenApiPhp/DefaultOperationId.php - -DIR=$(pwd) +section_start "Install domserver" make configure -./configure --with-baseurl='https://localhost/domjudge/' --enable-doc-build=no --prefix=$HOME/domjudge +./configure --with-baseurl='https://localhost/domjudge/' --enable-doc-build=no --prefix="/opt/domjudge" make domserver sudo make install-domserver +section_end +section_start "Explicit start mysql + install DB" sudo /etc/init.d/mysql start -$HOME/domjudge/domserver/bin/dj_setup_database -uroot -proot install +/opt/domjudge/domserver/bin/dj_setup_database -uroot -proot install +section_end -sudo cp $HOME/domjudge/domserver/etc/domjudge-fpm.conf /etc/php/7.4/fpm/pool.d/domjudge.conf -sudo systemctl restart php7.4-fpm -sudo systemctl status php7.4-fpm +section_start "Setup webserver" +sudo cp /opt/domjudge/domserver/etc/domjudge-fpm.conf /etc/php/7.4/fpm/pool.d/domjudge.conf sudo rm -f /etc/nginx/sites-enabled/* -sudo cp $HOME/domjudge/domserver/etc/nginx-conf /etc/nginx/sites-enabled/domjudge +sudo cp /opt/domjudge/domserver/etc/nginx-conf /etc/nginx/sites-enabled/domjudge openssl req -nodes -new -x509 -keyout /tmp/server.key -out /tmp/server.crt -subj "/C=NL/ST=Noord-Holland/L=Amsterdam/O=TestingForPR/CN=localhost" # shellcheck disable=SC2002 -cat $(pwd)/.github/workflowscripts/nginx_extra | sudo tee -a /etc/nginx/sites-enabled/domjudge +cat "$(pwd)/.github/workflowscripts/nginx_extra" | sudo tee -a /etc/nginx/sites-enabled/domjudge +sudo nginx -t +section_end -sudo systemctl restart nginx +section_start "Show webserver is up" +for service in nginx php7.4-fpm; do + sudo systemctl restart $service + sudo systemctl status $service +done +section_end diff --git a/.github/workflowscripts/nginx_extra b/.github/workflowscripts/nginx_extra index e2066355e7..cc31ef759a 100644 --- a/.github/workflowscripts/nginx_extra +++ b/.github/workflowscripts/nginx_extra @@ -5,5 +5,5 @@ server { ssl_session_timeout 5m; ssl_prefer_server_ciphers on; add_header Strict-Transport-Security max-age=31556952; - include /home/runner/domjudge/domserver/etc/nginx-conf-inner; + include /opt/domjudge/domserver/etc/nginx-conf-inner; } diff --git a/composer.lock b/composer.lock index 7f6e48ebba..ddae331a7c 100644 --- a/composer.lock +++ b/composer.lock @@ -2324,16 +2324,16 @@ }, { "name": "guzzlehttp/psr7", - "version": "1.8.5", + "version": "1.9.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "337e3ad8e5716c15f9657bd214d16cc5e69df268" + "reference": "e98e3e6d4f86621a9b75f623996e6bbdeb4b9318" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/337e3ad8e5716c15f9657bd214d16cc5e69df268", - "reference": "337e3ad8e5716c15f9657bd214d16cc5e69df268", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/e98e3e6d4f86621a9b75f623996e6bbdeb4b9318", + "reference": "e98e3e6d4f86621a9b75f623996e6bbdeb4b9318", "shasum": "" }, "require": { @@ -2354,7 +2354,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7-dev" + "dev-master": "1.9-dev" } }, "autoload": { @@ -2414,7 +2414,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/1.8.5" + "source": "https://github.com/guzzle/psr7/tree/1.9.0" }, "funding": [ { @@ -2430,7 +2430,7 @@ "type": "tidelift" } ], - "time": "2022-03-20T21:51:18+00:00" + "time": "2022-06-20T21:43:03+00:00" }, { "name": "http-interop/http-factory-guzzle", diff --git a/webapp/config/packages/nelmio_api_doc.yaml b/webapp/config/packages/nelmio_api_doc.yaml index 784a2af444..38b7ad70cb 100644 --- a/webapp/config/packages/nelmio_api_doc.yaml +++ b/webapp/config/packages/nelmio_api_doc.yaml @@ -1,5 +1,6 @@ nelmio_api_doc: documentation: + openapi: 3.1.0 info: title: DOMjudge description: DOMjudge API v4 @@ -19,6 +20,8 @@ nelmio_api_doc: required: true schema: type: string + pattern: "^[A-Za-z0-9]{1,255}$" + maxLength: 255 examples: int0: value: "2" @@ -36,6 +39,8 @@ nelmio_api_doc: required: true schema: type: integer + minimum: 1 + maximum: 9999 examples: balloon: value: 1 @@ -46,7 +51,10 @@ nelmio_api_doc: description: The ID of the entity required: true schema: + $ref: "#/components/schemas/Id" type: string + pattern: "^[A-Za-z0-9]{1,255}$" + maxLength: 255 examples: generic: value: "1" @@ -68,8 +76,7 @@ nelmio_api_doc: schema: type: array items: - type: string - description: A single ID + $ref: "#/components/schemas/Id" strict: name: strict in: query @@ -97,6 +104,10 @@ nelmio_api_doc: schema: type: string schemas: + Id: + type: string + pattern: "^[A-Za-z0-9]{1,255}$" + maxLength: 255 ImageList: type: array items: diff --git a/webapp/src/Controller/API/JudgehostController.php b/webapp/src/Controller/API/JudgehostController.php index 2e8b2d0761..f0076554ad 100644 --- a/webapp/src/Controller/API/JudgehostController.php +++ b/webapp/src/Controller/API/JudgehostController.php @@ -1288,6 +1288,44 @@ private function getTestcaseFiles(string $id): array /** * Fetch work tasks. * @Rest\Post("/fetch-work") + * @OA\RequestBody( + * description="The hostname of the judgedaemon requesting.", + * @OA\JsonContent( + * required={"hostname"}, + * @OA\Property( + * property="hostname", + * type="string", + * format="string", + * description="Hostname of judgedaemon" + * ), + * @OA\Property( + * property="max-batchsize", + * type="integer", + * format="integer", + * description="Maximum size judge requests to handle" + * ), + * @OA\Schema( + * @OA\Property( + * property="hostname", + * type="string", + * format="string", + * description="Hostname of judgedaemon" + * ), + * @OA\Property( + * property="max-batchsize", + * type="integer", + * format="integer", + * description="Maximum size judge requests to handle" + * ), + * ), + * @OA\Examples(example="example-data", value={"hostname": "example-judgehost1"}, summary="Fetch work with example judgedaemon."), + * ) + * ) + * @OA\Response( + * response="200", + * description="List of judgeTasks.", + * @OA\Schema(ref="#/definitions/JudgeTaskList") + * ) * @Security("is_granted('ROLE_JUDGEHOST')") */ public function getJudgeTasksAction(Request $request): array