Last active
March 31, 2025 12:04
-
-
Save tvlooy/f0254d00cc2bba0e4ff0afaa3f6a0459 to your computer and use it in GitHub Desktop.
Observability checks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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