opsdash-app/tools/security/run_curl_checks.sh

78 lines
3.1 KiB
Bash
Executable file

#!/usr/bin/env bash
set -euo pipefail
BASE=${OPSDASH_BASE:-http://localhost:8088/index.php/apps/opsdash}
LOAD_URL="$BASE/overview/load"
PERSIST_URL="$BASE/overview/persist"
NOTES_URL="$BASE/overview/notes"
PRESETS_URL="$BASE/overview/presets"
USER=${OPSDASH_USER:-admin}
PASS=${OPSDASH_PASS:-admin}
NC_ROOT=${NEXTCLOUD_ROOT:-${BASE%/apps/opsdash}}
LOGIN_URL="$NC_ROOT/login"
echo "[security] Using basic auth to hit OCS API (establish cookie-less session)"
AUTH_CURL=(curl -s -u "$USER:$PASS" -H 'OCS-APIREQUEST: true')
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TOKEN_HELPER="$SCRIPT_DIR/_token.sh"
if [[ -f "$TOKEN_HELPER" ]]; then
. "$TOKEN_HELPER"
elif [[ -f "$SCRIPT_DIR/../../opsdash/tools/security/_token.sh" ]]; then
. "$SCRIPT_DIR/../../opsdash/tools/security/_token.sh"
fi
TOKEN=""
if declare -F fetch_requesttoken >/dev/null; then
TOKEN=$(fetch_requesttoken "$BASE/.." "$USER" "$PASS" || true)
fi
json_get() {
"${AUTH_CURL[@]}" "$@"
}
json_post() {
"${AUTH_CURL[@]}" -H 'Content-Type: application/json' -X POST "$1" --data "$2"
}
print_section() {
printf '\n=== %s ===\n' "$1"
}
print_section "Range clamp"
json_get "$LOAD_URL?range=year&offset=0" | jq '.meta.range'
print_section "Offset clamp"
json_get "$LOAD_URL?range=week&offset=999" | jq '.meta.offset'
print_section "Unauthorized request"
curl -s -o /dev/null -w '%{http_code}\n' "$LOAD_URL?range=week&offset=0"
print_section "Missing OCS header / CSRF"
curl -s -u "$USER:$PASS" -H 'Content-Type: application/json' -X POST "$PERSIST_URL" --data '{}' -o /dev/null -w '%{http_code}\n'
print_section "Fuzz persist"
FZZ='{"cals":["personal"],"groups":{"personal":999},"targets_week":{"personal":-123},"targets_month":{"personal":987654321},"targets_config":{"totalHours":100000,"categories":[{"id":"<script>alert(1)</script>","label":"<img src=x onerror=alert(1)>","targetHours":-50,"includeWeekend":true}]},"theme_preference":"purple"}'
if [[ -n "$TOKEN" ]]; then
json_post "$PERSIST_URL" "$FZZ" -H "requesttoken: $TOKEN" | jq '.targets_config_read.totalHours, .targets_config_read.categories[0].id'
else
json_post "$PERSIST_URL" "$FZZ" | jq '.targets_config_read.totalHours, .targets_config_read.categories[0].id'
fi
print_section "Preset sanitisation"
PRESET_PAYLOAD='{"name":"evil<script>","selected":["personal"],"groups":{"personal":0},"targets_week":{"personal":5},"targets_month":{"personal":20},"targets_config":{"totalHours":5,"categories":[]}}'
SANITIZED_NAME=$(json_post "$PRESETS_URL" "$PRESET_PAYLOAD" | jq -r '.preset.name')
echo "Saved preset as: $SANITIZED_NAME"
curl -s -u "$USER:$PASS" -H 'OCS-APIREQUEST: true' "$PRESETS_URL/$SANITIZED_NAME" | jq '.preset.name'
if [[ -n "$TOKEN" ]]; then
curl -s -u "$USER:$PASS" -H 'OCS-APIREQUEST: true' -H "requesttoken: $TOKEN" -X DELETE "$PRESETS_URL/$SANITIZED_NAME" | jq '.ok'
else
curl -s -u "$USER:$PASS" -H 'OCS-APIREQUEST: true' -X DELETE "$PRESETS_URL/$SANITIZED_NAME" | jq '.ok'
fi
print_section "Notes injection"
NOTE=$(cat <<'JSON'
{"range":"week","offset":0,"content":"<script>alert('note')</script>"}
JSON
)
json_post "$NOTES_URL" "$NOTE" | jq '.ok'
json_get "$NOTES_URL?range=week&offset=0" | jq '.notes.current'