diff --git a/merge-request-check/Dockerfile b/merge-request-check/Dockerfile index a1cae24c350f64ab2e6c2e5c385633b7cd59960f..c42327ba786f2b1494643210544f9704045be32f 100644 --- a/merge-request-check/Dockerfile +++ b/merge-request-check/Dockerfile @@ -14,9 +14,12 @@ LABEL org.label-schema.build-date=$BUILD_DATE \ RUN mkdir /code WORKDIR /code +RUN apk add --no-cache bash git + COPY requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt COPY approval_check.py ./ +COPY merge_review_check.sh ./ CMD ["python", "approval_check.py"] diff --git a/merge-request-check/approval_check.py b/merge-request-check/approval_check.py index 628158ddf6303413ea5fa165c43f3ea1b574c5a6..45e70197a8b2137bba7b5ce02718ad5b99d5fc30 100644 --- a/merge-request-check/approval_check.py +++ b/merge-request-check/approval_check.py @@ -1,5 +1,6 @@ import os import requests +import subprocess import sys ''' @@ -18,11 +19,23 @@ response = requests.post('https://its-lampprod1-whm.unl.edu/merge_auth_check.php 'user': os.environ.get('GITLAB_USER_LOGIN') }) -if (response.status_code == 200): +if response.status_code == 200: # Merge is allowed, print response and exit cleanly (status 0) print(response.content.decode('utf-8')) exit(0) +elif response.status_code == 403: + # Merge is not allowed, run further checks to see if a review is needed. + result = subprocess.run(['bash', '/code/merge_review_check.sh'], capture_output=True, text=True) + + # Print output from additional checks script + print(result.stdout) + print(result.stderr, file=sys.stderr) + + # Exit using the exit code form the check script + exit(result.returncode) else: - # Merge is not allowed, print response and exit with an error (status 1) + # API call failed. Server may be down, or other setup failure. Exit with an error (status 1) + print('Merge request approval check API call failed') + print(f'API Call Status Code: {response.status_code}') print(response.content.decode('utf-8'), file=sys.stderr) exit(1) diff --git a/merge-request-check/merge_review_check.sh b/merge-request-check/merge_review_check.sh new file mode 100644 index 0000000000000000000000000000000000000000..016b541d0fe1554288eadac7167a7a3f18361bd2 --- /dev/null +++ b/merge-request-check/merge_review_check.sh @@ -0,0 +1,150 @@ +#!/bin/bash + +# Script to check changed file extensions and for code in changed lines of files + +# Define variables for testing only (these are passed in via GitLab CI) +#CI_COMMIT_BEFORE_SHA="" +#CI_COMMIT_SHA="" + +# For merge request pipelines, the before SHA is all 0s. +# Replace with a MR specific variable for these pipelines. +if [[ $CI_PIPELINE_SOURCE = 'merge_request_event' ]]; then + CI_COMMIT_BEFORE_SHA="$CI_MERGE_REQUEST_DIFF_BASE_SHA" +fi + +# If any commands in this scprt fail, exit with a non-zero exit code +set -e + +# Function to check for ASP.NET controls and directives +check_aspx() { + local content="$1" + + # Check for ASP.NET server controls and directives (matches <asp:, <%@ Page, <%@ Control, etc.) + if echo "$content" | grep -qE '<asp:|<%|%>'; then + return 0 # ASP.NET control or directive found + fi + + return 1 # No ASP.NET control or directive found +} + +# Function to check for PHP code and Blade directives +check_php_blade() { + local content="$1" + + # Check for PHP code (matches <?php ... ?> and <?= ... ?>) + if echo "$content" | grep -qE '<\?php|<\?='; then + return 0 # PHP code found + fi + + # Check for Blade directives (matches {{ ... }}, {!! ... !!}, @if, @foreach, etc.) + if echo "$content" | grep -qE '{{.*}}|{!!.*!!}|@\w+'; then + return 0 # Blade directive found + fi + + return 1 # No PHP code or Blade directive found +} + +# Flag to track if code was found +code_found=0 + +# Loop through each changed file in the commit +tempfile=$(mktemp) +git diff --name-only $CI_COMMIT_BEFORE_SHA..$CI_COMMIT_SHA > "$tempfile" +while read -r file; do + case "$file" in + *.html) + echo "OK: HTML file change - $file" + ;; + *.js) + echo "OK: JS file change - $file" + ;; + *.css) + echo "OK: CSS file change - $file" + ;; + *.pdf) + echo "OK: PDF file change - $file" + ;; + *.doc) + echo "OK: DOC file change - $file" + ;; + *.docx) + echo "OK: DOCX file change - $file" + ;; + *.png) + echo "OK: PNG file change - $file" + ;; + *.jpg) + echo "OK: JPG file change - $file" + ;; + *.gif) + echo "OK: GIF file change - $file" + ;; + *.svg) + echo "OK: SVG file change - $file" + ;; + *.ico) + echo "OK: ICO file change - $file" + ;; + *.md) + echo "OK: MD file change - $file" + ;; + *.csproj) + echo "OK: CSPROJ file change - $file" + ;; + *.sln) + echo "OK: SLN file change - $file" + ;; + *.aspx) + # need to look for code within the HTML + echo "CHECKING: ASPX file - $file" + + while IFS= read -r line; do + if [[ $line == \+\ * ]]; then + # Extract added lines, ignoring leading '+' + content=$(echo "$line" | cut -c2-) + + # Check for ASP.NET code and elements + if check_aspx "$content"; then + trimmed=$(echo "$content" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + echo "FAIL: Found ASP.NET code or elements in $file: $trimmed" + code_found=1 # Set flag indicating code was found + fi + fi + done < <(git diff $CI_COMMIT_BEFORE_SHA..$CI_COMMIT_SHA -- "$file") + ;; + *.blade.php) + # need to look for code within the HTML + echo "CHECKING: BLADE.PHP file - $file" + + while IFS= read -r line; do + if [[ $line == \+\ * ]]; then + # Extract added lines, ignoring leading '+' + content=$(echo "$line" | cut -c2-) + + # Check for PHP code and blade directives + if check_php_blade "$content"; then + trimmed=$(echo "$content" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + echo "FAIL: Found PHP code or Blade directives in $file: $trimmed" + code_found=1 # Set flag indicating code was found + fi + fi + done < <(git diff $CI_COMMIT_BEFORE_SHA..$CI_COMMIT_SHA -- "$file") + ;; + *) + # All other files fail for manual review + echo "FAIL: File type needs reviewed - $file" + code_found=1 # Set flag indicating code could be present + ;; + esac +done < "$tempfile" + +rm "$tempfile" + +# Fail the GitLab CI job if code was found +if [ $code_found -eq 1 ]; then + echo "Job failed. Files found that need to be reviewed. Please contact ITS-ADS to request a review of this deployment." + exit 1 # Exit with non-zero status to indicate failure +else + echo "Job succeeded. No files that need to be reviewed." + exit 0 # Exit with zero status to indicate success +fi \ No newline at end of file diff --git a/merge-request-check/requirements.txt b/merge-request-check/requirements.txt index e20605c43d4af3b7205bb2ebb1c1c5f2fc7fc84f..05cabde8c72ded63ffd9156daceddef69df13348 100644 --- a/merge-request-check/requirements.txt +++ b/merge-request-check/requirements.txt @@ -1 +1,6 @@ -requests==2.22.0 \ No newline at end of file +certifi==2024.7.4 +chardet==3.0.4 +charset-normalizer==3.3.2 +idna==2.8 +requests==2.32.3 +urllib3==2.2.2