Skip to content

Instantly share code, notes, and snippets.

@tvlooy
Last active March 31, 2025 12:04
Show Gist options
  • Save tvlooy/f0254d00cc2bba0e4ff0afaa3f6a0459 to your computer and use it in GitHub Desktop.
Save tvlooy/f0254d00cc2bba0e4ff0afaa3f6a0459 to your computer and use it in GitHub Desktop.
Observability checks
#!/bin/bash
# https://ctors.net/isc_license.txt
if [ -z "$2" ]; then
echo "Usage: $0 <domain> <result.xml>"
exit 2
fi
DOMAIN=$1
RESULT_XML=$2
curl='curl -s -L -D - '
headers=(
X-XSS-Protection
X-Frame-Options
X-Content-Type-Options
Strict-Transport-Security
Referrer-Policy
)
DOMAIN=$(curl -Ls -o /dev/null -w %{url_effective} $DOMAIN)
echo "Using final redirect $DOMAIN"
ip=$(dig +short `echo $DOMAIN | sed 's#https://##' | sed 's#http://##' | cut -d/ -f1` | tail -1)
if [ -z $ip ]; then
echo $DOMAIN | sed 's/https://' | sed 's/http://' | sed 's#/##g' | xargs host
exit 1
fi
net=$(whois $ip | grep -i netname | cut -d: -f2 | xargs)
echo "Found IP $ip ($net)"
echo
output=$($curl $DOMAIN)
# Initialize JUnit XML output
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" > $RESULT_XML
echo "<testsuites>" >> $RESULT_XML
echo " <testsuite name=\"Security Headers Scan\" tests=\"${#headers[@]}\" failures=\"0\" errors=\"0\" time=\"0\">" >> $RESULT_XML
test_failures=0
for header in ${headers[@]}; do
header_count=$(echo "$output" | grep -i $header | grep -v \<meta | wc -l)
result=$(echo "$output" | grep -i $header | grep -v \<meta)
value=$(echo "$output" | grep -i $header | grep -v \<meta | tr '[:upper:]' '[:lower:]' | cut -d: -f2- | tr -d '[:space:]')
testcase_result=""
failure_message=""
case $header in
X-XSS-Protection)
if [[ -z $result ]] || [[ $value == '0' ]]; then
testcase_result="<testcase name=\"$header\" classname=\"SecurityHeaders\" time=\"0\"/>"
else
testcase_result="<testcase name=\"$header\" classname=\"SecurityHeaders\" time=\"0\">"
failure_message="<failure message=\"Expected unset or '0', got '$value'\"></failure>"
test_failures=$((test_failures+1))
fi
;;
X-Frame-Options)
if [[ $value == 'sameorigin' ]]; then
testcase_result="<testcase name=\"$header\" classname=\"SecurityHeaders\" time=\"0\"/>"
else
testcase_result="<testcase name=\"$header\" classname=\"SecurityHeaders\" time=\"0\">"
failure_message="<failure message=\"Expected 'SAMEORIGIN', got '$value'\"></failure>"
test_failures=$((test_failures+1))
fi
;;
X-Content-Type-Options)
if [[ $value == 'nosniff' ]]; then
testcase_result="<testcase name=\"$header\" classname=\"SecurityHeaders\" time=\"0\"/>"
else
testcase_result="<testcase name=\"$header\" classname=\"SecurityHeaders\" time=\"0\">"
failure_message="<failure message=\"Expected 'nosniff', got '$value'\"></failure>"
test_failures=$((test_failures+1))
fi
;;
Strict-Transport-Security)
test_value=$(echo $value | sed 's/includesubdomains//g;s/preload//g;s/;//g;s/ //g;')
if [[ $test_value == "max-age=31536000" ]]; then
testcase_result="<testcase name=\"$header\" classname=\"SecurityHeaders\" time=\"0\"/>"
else
testcase_result="<testcase name=\"$header\" classname=\"SecurityHeaders\" time=\"0\">"
failure_message="<failure message=\"Expected 'max-age=31536000', got '$value'\"></failure>"
test_failures=$((test_failures+1))
fi
;;
Referrer-Policy)
if [[ -z $result ]]; then
testcase_result="<testcase name=\"$header\" classname=\"SecurityHeaders\" time=\"0\">"
failure_message="<failure message=\"Expected to be set, but missing\"></failure>"
test_failures=$((test_failures+1))
else
testcase_result="<testcase name=\"$header\" classname=\"SecurityHeaders\" time=\"0\"/>"
fi
;;
esac
echo " $testcase_result" >> $RESULT_XML
if [[ ! -z $failure_message ]]; then
echo " $failure_message" >> $RESULT_XML
echo " </testcase>" >> $RESULT_XML
fi
done
echo " </testsuite>" >> $RESULT_XML
echo "</testsuites>" >> $RESULT_XML
# Update test failures count in XML
sed -i "s/failures=\"0\"/failures=\"$test_failures\"/" $RESULT_XML
if [ $test_failures -gt 0 ]; then
echo "FAILURES FOUND WHILE CHECKING HEADERS"
exit 1
fi
#!/bin/bash
# https://ctors.net/isc_license.txt
if [ -z "$3" ]; then
echo "Usage: $0 <domain> <treshold> <result.xml>"
exit 2
fi
DOMAIN=$1
THRESHOLD=${2:-100}
RESULT_XML=$3
ENDPOINT="https://observatory-api.mdn.mozilla.net/api/v2/scan?host=${DOMAIN}"
JSON=$(curl -s -X POST "$ENDPOINT")
# Extract values using jq
ID=$(echo "$JSON" | jq -r '.id')
DETAILS_URL=$(echo "$JSON" | jq -r '.details_url')
ALGORITHM_VERSION=$(echo "$JSON" | jq -r '.algorithm_version')
SCANNED_AT=$(echo "$JSON" | jq -r '.scanned_at')
GRADE=$(echo "$JSON" | jq -r '.grade')
SCORE=$(echo "$JSON" | jq -r '.score')
STATUS_CODE=$(echo "$JSON" | jq -r '.status_code')
TESTS_FAILED=$(echo "$JSON" | jq -r '.tests_failed')
TESTS_PASSED=$(echo "$JSON" | jq -r '.tests_passed')
TESTS_QUANTITY=$(echo "$JSON" | jq -r '.tests_quantity')
# Validate that SCORE is a number
if ! [[ "$SCORE" =~ ^[0-9]+$ ]]; then
echo "Error: Invalid score received: $SCORE"
exit 2
fi
# Calculate test failures
FAILURES=$((TESTS_QUANTITY - TESTS_PASSED))
# Generate JUnit XML
cat <<EOF > $RESULT_XML
<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
<testsuite name="Mozilla Observatory Scan" tests="$TESTS_QUANTITY" failures="$FAILURES" errors="0" time="0">
<properties>
<property name="id" value="$ID"/>
<property name="details_url" value="$DETAILS_URL"/>
<property name="algorithm_version" value="$ALGORITHM_VERSION"/>
<property name="scanned_at" value="$SCANNED_AT"/>
<property name="grade" value="$GRADE"/>
<property name="score" value="$SCORE"/>
<property name="status_code" value="$STATUS_CODE"/>
</properties>
<testcase name="Observatory Scan Result" classname="MozillaObservatory" time="0">
EOF
if [ "$FAILURES" -gt 0 ]; then
echo " <failure message=\"Some tests failed\"></failure>" >> $RESULT_XML
fi
cat <<EOF >> $RESULT_XML
</testcase>
</testsuite>
</testsuites>
EOF
# Compare score with threshold
if [ "$SCORE" -ge "$THRESHOLD" ]; then
echo "Scoring of $DOMAIN is $SCORE and ABOVE threshold $THRESHOLD"
exit 0
else
echo "Scoring of $DOMAIN is $SCORE and BELOW threshold $THRESHOLD"
echo "More info at $DETAILS_URL"
exit 1
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment