diff --git a/rpi/smoke-test.sh b/rpi/smoke-test.sh index 5940096..9d2247c 100755 --- a/rpi/smoke-test.sh +++ b/rpi/smoke-test.sh @@ -20,23 +20,23 @@ NC='\033[0m' PI_IP="${1:-192.168.0.4}" HTTPS=false if [[ "$2" == "--https" ]] || [[ "$1" == "--https" ]]; then - HTTPS=true - if [[ "$1" == "--https" ]]; then - PI_IP="192.168.0.4" - fi + HTTPS=true + if [[ "$1" == "--https" ]]; then + PI_IP="192.168.0.4" + fi fi if [ "$HTTPS" = true ]; then - BASE_URL="https://$PI_IP:5000" - CURL_OPTS="-k" # Allow self-signed certs + BASE_URL="https://$PI_IP:5000" + CURL_OPTS="-k" # Allow self-signed certs else - BASE_URL="http://$PI_IP:5000" - CURL_OPTS="" + BASE_URL="http://$PI_IP:5000" + CURL_OPTS="" fi # Test credentials -ADMIN_USER="smokeadmin" -ADMIN_PASS="SmokeAdmin123!" +ADMIN_USER="admin" +ADMIN_PASS="stegasoo" REGULAR_USER="smokeuser" REGULAR_PASS="SmokeUser123!" @@ -51,24 +51,24 @@ ENCODED_IMAGE_USER=$(mktemp --suffix=.png) QR_IMAGE=$(mktemp --suffix=.png) cleanup() { - rm -f "$COOKIE_JAR" "$COOKIE_JAR_USER" "$TEST_IMAGE" "$ENCODED_IMAGE" "$ENCODED_IMAGE_USER" "$QR_IMAGE" "$RESPONSE" + rm -f "$COOKIE_JAR" "$COOKIE_JAR_USER" "$TEST_IMAGE" "$ENCODED_IMAGE" "$ENCODED_IMAGE_USER" "$QR_IMAGE" "$RESPONSE" } trap cleanup EXIT # Create a simple test image (red square) create_test_image() { - if command -v convert &>/dev/null; then - convert -size 100x100 xc:red "$TEST_IMAGE" - elif command -v python3 &>/dev/null; then - python3 -c " + if command -v convert &>/dev/null; then + convert -size 100x100 xc:red "$TEST_IMAGE" + elif command -v python3 &>/dev/null; then + python3 -c " from PIL import Image img = Image.new('RGB', (100, 100), color='red') img.save('$TEST_IMAGE') " - else - echo -e "${YELLOW}Warning: No image tool available, skipping encode/decode tests${NC}" - return 1 - fi + else + echo -e "${YELLOW}Warning: No image tool available, skipping encode/decode tests${NC}" + return 1 + fi } # Results tracking @@ -76,17 +76,17 @@ TESTS_PASSED=0 TESTS_FAILED=0 pass() { - echo -e " ${GREEN}[PASS]${NC} $1" - TESTS_PASSED=$((TESTS_PASSED + 1)) + echo -e " ${GREEN}[PASS]${NC} $1" + TESTS_PASSED=$((TESTS_PASSED + 1)) } fail() { - echo -e " ${RED}[FAIL]${NC} $1" - TESTS_FAILED=$((TESTS_FAILED + 1)) + echo -e " ${RED}[FAIL]${NC} $1" + TESTS_FAILED=$((TESTS_FAILED + 1)) } skip() { - echo -e " ${YELLOW}[SKIP]${NC} $1" + echo -e " ${YELLOW}[SKIP]${NC} $1" } # ============================================================================= @@ -108,30 +108,30 @@ echo "" echo -e "${BOLD}[1/9] Web UI Accessibility${NC}" if curl $CURL_OPTS -s -o /dev/null -w "%{http_code}" "$BASE_URL" | grep -q "200\|302"; then - pass "Web UI is reachable" + pass "Web UI is reachable" else - fail "Web UI not reachable at $BASE_URL" - echo -e "${RED}Cannot continue without web access. Is the Pi running?${NC}" - exit 1 + fail "Web UI not reachable at $BASE_URL" + echo -e "${RED}Cannot continue without web access. Is the Pi running?${NC}" + exit 1 fi # Check if redirected to setup (first run) or login REDIRECT=$(curl $CURL_OPTS -s -o /dev/null -w "%{redirect_url}" "$BASE_URL") if echo "$REDIRECT" | grep -q "setup"; then - pass "Redirected to setup (fresh install)" - NEEDS_SETUP=true + pass "Redirected to setup (fresh install)" + NEEDS_SETUP=true elif echo "$REDIRECT" | grep -q "login"; then - pass "Redirected to login (already configured)" - NEEDS_SETUP=false + pass "Redirected to login (already configured)" + NEEDS_SETUP=false else - # Check page content - if curl $CURL_OPTS -s "$BASE_URL" | grep -q "setup\|Setup\|Create.*Admin"; then - pass "Setup page detected" - NEEDS_SETUP=true - else - pass "Login page detected" - NEEDS_SETUP=false - fi + # Check page content + if curl $CURL_OPTS -s "$BASE_URL" | grep -q "setup\|Setup\|Create.*Admin"; then + pass "Setup page detected" + NEEDS_SETUP=true + else + pass "Login page detected" + NEEDS_SETUP=false + fi fi # ============================================================================= @@ -142,35 +142,35 @@ echo "" echo -e "${BOLD}[2/9] Admin Setup${NC}" if [ "$NEEDS_SETUP" = true ]; then - # Get CSRF token from setup page - SETUP_PAGE=$(curl $CURL_OPTS -s -c "$COOKIE_JAR" "$BASE_URL/setup") - CSRF_TOKEN=$(echo "$SETUP_PAGE" | grep -oP 'name="csrf_token"[^>]*value="\K[^"]+' || echo "") + # Get CSRF token from setup page + SETUP_PAGE=$(curl $CURL_OPTS -s -c "$COOKIE_JAR" "$BASE_URL/setup") + CSRF_TOKEN=$(echo "$SETUP_PAGE" | grep -oP 'name="csrf_token"[^>]*value="\K[^"]+' || echo "") - if [ -z "$CSRF_TOKEN" ]; then - # Try alternate pattern - CSRF_TOKEN=$(echo "$SETUP_PAGE" | grep -oP 'csrf_token.*?value="\K[^"]+' || echo "") - fi + if [ -z "$CSRF_TOKEN" ]; then + # Try alternate pattern + CSRF_TOKEN=$(echo "$SETUP_PAGE" | grep -oP 'csrf_token.*?value="\K[^"]+' || echo "") + fi - # Create admin user - HTTP_CODE=$(curl $CURL_OPTS -s -o "$RESPONSE" -w "%{http_code}" \ - -b "$COOKIE_JAR" -c "$COOKIE_JAR" \ - -X POST "$BASE_URL/setup" \ - -d "username=$ADMIN_USER" \ - -d "password=$ADMIN_PASS" \ - -d "password_confirm=$ADMIN_PASS" \ - -d "csrf_token=$CSRF_TOKEN") + # Create admin user + HTTP_CODE=$(curl $CURL_OPTS -s -o "$RESPONSE" -w "%{http_code}" \ + -b "$COOKIE_JAR" -c "$COOKIE_JAR" \ + -X POST "$BASE_URL/setup" \ + -d "username=$ADMIN_USER" \ + -d "password=$ADMIN_PASS" \ + -d "password_confirm=$ADMIN_PASS" \ + -d "csrf_token=$CSRF_TOKEN") - if [ "$HTTP_CODE" = "302" ] || [ "$HTTP_CODE" = "200" ]; then - if curl $CURL_OPTS -s "$BASE_URL" | grep -q "login\|Login"; then - pass "Admin user created successfully" - else - pass "Setup completed (assuming success)" - fi + if [ "$HTTP_CODE" = "302" ] || [ "$HTTP_CODE" = "200" ]; then + if curl $CURL_OPTS -s "$BASE_URL" | grep -q "login\|Login"; then + pass "Admin user created successfully" else - fail "Failed to create admin user (HTTP $HTTP_CODE)" + pass "Setup completed (assuming success)" fi + else + fail "Failed to create admin user (HTTP $HTTP_CODE)" + fi else - skip "Setup already complete" + skip "Setup already complete" fi # ============================================================================= @@ -186,20 +186,20 @@ CSRF_TOKEN=$(echo "$LOGIN_PAGE" | grep -oP 'name="csrf_token"[^>]*value="\K[^"]+ # Try login as admin HTTP_CODE=$(curl $CURL_OPTS -s -o "$RESPONSE" -w "%{http_code}" \ - -b "$COOKIE_JAR" -c "$COOKIE_JAR" \ - -X POST "$BASE_URL/login" \ - -d "username=$ADMIN_USER" \ - -d "password=$ADMIN_PASS" \ - -d "csrf_token=$CSRF_TOKEN" \ - -L) + -b "$COOKIE_JAR" -c "$COOKIE_JAR" \ + -X POST "$BASE_URL/login" \ + -d "username=$ADMIN_USER" \ + -d "password=$ADMIN_PASS" \ + -d "csrf_token=$CSRF_TOKEN" \ + -L) # Check if we're logged in by accessing a protected page if curl $CURL_OPTS -s -b "$COOKIE_JAR" "$BASE_URL/" | grep -qi "encode\|decode\|logout"; then - pass "Admin login successful" - ADMIN_LOGGED_IN=true + pass "Admin login successful" + ADMIN_LOGGED_IN=true else - fail "Admin login failed" - ADMIN_LOGGED_IN=false + fail "Admin login failed" + ADMIN_LOGGED_IN=false fi # ============================================================================= @@ -210,55 +210,84 @@ echo "" echo -e "${BOLD}[4/9] Admin Encode/Decode${NC}" if [ "$ADMIN_LOGGED_IN" = true ]; then - ENCODE_PAGE=$(curl $CURL_OPTS -s -b "$COOKIE_JAR" "$BASE_URL/encode") + ENCODE_PAGE=$(curl $CURL_OPTS -s -b "$COOKIE_JAR" "$BASE_URL/encode") - if echo "$ENCODE_PAGE" | grep -qi "encode\|message\|image\|upload"; then - pass "Encode page loads" - else - fail "Encode page not accessible" + if echo "$ENCODE_PAGE" | grep -qi "encode\|message\|image\|upload"; then + pass "Encode page loads" + else + fail "Encode page not accessible" + fi + + # Try actual encoding if we have image tools + if create_test_image 2>/dev/null; then + CSRF_TOKEN=$(echo "$ENCODE_PAGE" | grep -oP 'name="csrf_token"[^>]*value="\K[^"]+' || echo "") + + # For encode: use same image as reference_photo and carrier (for simplicity) + # First POST (no redirect follow), get Location header, then GET result page + ENCODE_RESULT=$(curl $CURL_OPTS -s -D - -o /dev/null \ + -b "$COOKIE_JAR" -c "$COOKIE_JAR" \ + -X POST "$BASE_URL/encode" \ + -F "reference_photo=@$TEST_IMAGE" \ + -F "carrier=@$TEST_IMAGE" \ + -F "message=Admin smoke test" \ + -F "passphrase=smoke test phrase" \ + -F "pin=123456" \ + -F "csrf_token=$CSRF_TOKEN") + + # Extract redirect location + RESULT_LOCATION=$(echo "$ENCODE_RESULT" | grep -i "^location:" | tr -d '\r' | awk '{print $2}') + + if [ -n "$RESULT_LOCATION" ]; then + # GET the result page + RESULT_PAGE=$(curl $CURL_OPTS -s -b "$COOKIE_JAR" "$BASE_URL$RESULT_LOCATION") + + # Look for download link in result page + DOWNLOAD_URL=$(echo "$RESULT_PAGE" | grep -oP 'href="(/encode/download/[^"]+)"' | head -1 | grep -oP '/encode/download/[^"]+') fi - # Try actual encoding if we have image tools - if create_test_image 2>/dev/null; then - CSRF_TOKEN=$(echo "$ENCODE_PAGE" | grep -oP 'name="csrf_token"[^>]*value="\K[^"]+' || echo "") + if [ -n "$DOWNLOAD_URL" ]; then + # Download the encoded image + HTTP_CODE=$(curl $CURL_OPTS -s -o "$ENCODED_IMAGE" -w "%{http_code}" \ + -b "$COOKIE_JAR" "$BASE_URL$DOWNLOAD_URL") - HTTP_CODE=$(curl $CURL_OPTS -s -o "$ENCODED_IMAGE" -w "%{http_code}" \ - -b "$COOKIE_JAR" \ - -X POST "$BASE_URL/encode" \ - -F "image=@$TEST_IMAGE" \ - -F "message=Admin smoke test" \ - -F "csrf_token=$CSRF_TOKEN") + if [ "$HTTP_CODE" = "200" ] && file "$ENCODED_IMAGE" | grep -qi "image\|PNG\|JPEG"; then + pass "Admin encoding works" - if [ "$HTTP_CODE" = "200" ] && [ -s "$ENCODED_IMAGE" ]; then - if file "$ENCODED_IMAGE" | grep -qi "image\|PNG\|JPEG"; then - pass "Admin encoding works" + # Now decode it + DECODE_PAGE=$(curl $CURL_OPTS -s -b "$COOKIE_JAR" "$BASE_URL/decode") + CSRF_TOKEN=$(echo "$DECODE_PAGE" | grep -oP 'name="csrf_token"[^>]*value="\K[^"]+' || echo "") - # Now decode it - DECODE_PAGE=$(curl $CURL_OPTS -s -b "$COOKIE_JAR" "$BASE_URL/decode") - CSRF_TOKEN=$(echo "$DECODE_PAGE" | grep -oP 'name="csrf_token"[^>]*value="\K[^"]+' || echo "") + DECODED=$(curl $CURL_OPTS -s \ + -b "$COOKIE_JAR" \ + -X POST "$BASE_URL/decode" \ + -F "reference_photo=@$TEST_IMAGE" \ + -F "stego_image=@$ENCODED_IMAGE" \ + -F "passphrase=smoke test phrase" \ + -F "pin=123456" \ + -F "csrf_token=$CSRF_TOKEN") - DECODED=$(curl $CURL_OPTS -s \ - -b "$COOKIE_JAR" \ - -X POST "$BASE_URL/decode" \ - -F "image=@$ENCODED_IMAGE" \ - -F "csrf_token=$CSRF_TOKEN") - - if echo "$DECODED" | grep -q "Admin smoke test"; then - pass "Admin decoding works" - else - fail "Admin decode failed" - fi - else - fail "Encoding returned non-image response" - fi + if echo "$DECODED" | grep -q "Admin smoke test"; then + pass "Admin decoding works" else - fail "Encoding request failed (HTTP $HTTP_CODE)" + fail "Admin decode failed" fi + else + fail "Failed to download encoded image (HTTP $HTTP_CODE)" + fi else - skip "Encode/Decode (no image tools)" + # Check for error messages in result page + ERROR_MSG=$(echo "$RESULT_PAGE" | grep -oP 'toast-body">[^<]*<[^>]*>[^<]*' | head -1) + if [ -n "$ERROR_MSG" ]; then + fail "Encoding failed: $ERROR_MSG" + else + fail "No download link found in encode result" + fi fi + else + skip "Encode/Decode (no image tools)" + fi else - skip "Admin encode/decode (not logged in)" + skip "Admin encode/decode (not logged in)" fi # ============================================================================= @@ -269,48 +298,48 @@ echo "" echo -e "${BOLD}[5/9] Create Regular User${NC}" if [ "$ADMIN_LOGGED_IN" = true ]; then - # Check if there's a user management page - USERS_PAGE=$(curl $CURL_OPTS -s -b "$COOKIE_JAR" "$BASE_URL/users" 2>/dev/null || echo "") + # Check if there's a user management page + USERS_PAGE=$(curl $CURL_OPTS -s -b "$COOKIE_JAR" "$BASE_URL/users" 2>/dev/null || echo "") - if echo "$USERS_PAGE" | grep -qi "user\|create\|add"; then - CSRF_TOKEN=$(echo "$USERS_PAGE" | grep -oP 'name="csrf_token"[^>]*value="\K[^"]+' || echo "") + if echo "$USERS_PAGE" | grep -qi "user\|create\|add"; then + CSRF_TOKEN=$(echo "$USERS_PAGE" | grep -oP 'name="csrf_token"[^>]*value="\K[^"]+' || echo "") - HTTP_CODE=$(curl $CURL_OPTS -s -o "$RESPONSE" -w "%{http_code}" \ - -b "$COOKIE_JAR" \ - -X POST "$BASE_URL/users/create" \ - -d "username=$REGULAR_USER" \ - -d "password=$REGULAR_PASS" \ - -d "password_confirm=$REGULAR_PASS" \ - -d "csrf_token=$CSRF_TOKEN") + HTTP_CODE=$(curl $CURL_OPTS -s -o "$RESPONSE" -w "%{http_code}" \ + -b "$COOKIE_JAR" \ + -X POST "$BASE_URL/users/create" \ + -d "username=$REGULAR_USER" \ + -d "password=$REGULAR_PASS" \ + -d "password_confirm=$REGULAR_PASS" \ + -d "csrf_token=$CSRF_TOKEN") - if [ "$HTTP_CODE" = "302" ] || [ "$HTTP_CODE" = "200" ]; then - pass "Regular user created" - USER_CREATED=true - else - # Try alternate endpoint - HTTP_CODE=$(curl $CURL_OPTS -s -o "$RESPONSE" -w "%{http_code}" \ - -b "$COOKIE_JAR" \ - -X POST "$BASE_URL/register" \ - -d "username=$REGULAR_USER" \ - -d "password=$REGULAR_PASS" \ - -d "password_confirm=$REGULAR_PASS" \ - -d "csrf_token=$CSRF_TOKEN") - - if [ "$HTTP_CODE" = "302" ] || [ "$HTTP_CODE" = "200" ]; then - pass "Regular user created (via register)" - USER_CREATED=true - else - fail "Failed to create regular user" - USER_CREATED=false - fi - fi + if [ "$HTTP_CODE" = "302" ] || [ "$HTTP_CODE" = "200" ]; then + pass "Regular user created" + USER_CREATED=true else - skip "User creation (no user management page)" + # Try alternate endpoint + HTTP_CODE=$(curl $CURL_OPTS -s -o "$RESPONSE" -w "%{http_code}" \ + -b "$COOKIE_JAR" \ + -X POST "$BASE_URL/register" \ + -d "username=$REGULAR_USER" \ + -d "password=$REGULAR_PASS" \ + -d "password_confirm=$REGULAR_PASS" \ + -d "csrf_token=$CSRF_TOKEN") + + if [ "$HTTP_CODE" = "302" ] || [ "$HTTP_CODE" = "200" ]; then + pass "Regular user created (via register)" + USER_CREATED=true + else + fail "Failed to create regular user" USER_CREATED=false + fi fi -else - skip "User creation (admin not logged in)" + else + skip "User creation (no user management page)" USER_CREATED=false + fi +else + skip "User creation (admin not logged in)" + USER_CREATED=false fi # ============================================================================= @@ -321,47 +350,50 @@ echo "" echo -e "${BOLD}[6/9] Regular User Workflow${NC}" if [ "$USER_CREATED" = true ]; then - # Logout admin first (get fresh session) - curl $CURL_OPTS -s -b "$COOKIE_JAR" "$BASE_URL/logout" > /dev/null + # Logout admin first (get fresh session) + curl $CURL_OPTS -s -b "$COOKIE_JAR" "$BASE_URL/logout" >/dev/null - # Login as regular user - LOGIN_PAGE=$(curl $CURL_OPTS -s -c "$COOKIE_JAR_USER" "$BASE_URL/login") - CSRF_TOKEN=$(echo "$LOGIN_PAGE" | grep -oP 'name="csrf_token"[^>]*value="\K[^"]+' || echo "") + # Login as regular user + LOGIN_PAGE=$(curl $CURL_OPTS -s -c "$COOKIE_JAR_USER" "$BASE_URL/login") + CSRF_TOKEN=$(echo "$LOGIN_PAGE" | grep -oP 'name="csrf_token"[^>]*value="\K[^"]+' || echo "") - HTTP_CODE=$(curl $CURL_OPTS -s -o "$RESPONSE" -w "%{http_code}" \ - -b "$COOKIE_JAR_USER" -c "$COOKIE_JAR_USER" \ - -X POST "$BASE_URL/login" \ - -d "username=$REGULAR_USER" \ - -d "password=$REGULAR_PASS" \ - -d "csrf_token=$CSRF_TOKEN" \ - -L) + HTTP_CODE=$(curl $CURL_OPTS -s -o "$RESPONSE" -w "%{http_code}" \ + -b "$COOKIE_JAR_USER" -c "$COOKIE_JAR_USER" \ + -X POST "$BASE_URL/login" \ + -d "username=$REGULAR_USER" \ + -d "password=$REGULAR_PASS" \ + -d "csrf_token=$CSRF_TOKEN" \ + -L) - if curl $CURL_OPTS -s -b "$COOKIE_JAR_USER" "$BASE_URL/" | grep -qi "encode\|decode\|logout"; then - pass "Regular user login successful" + if curl $CURL_OPTS -s -b "$COOKIE_JAR_USER" "$BASE_URL/" | grep -qi "encode\|decode\|logout"; then + pass "Regular user login successful" - # Try encode/decode as regular user - if [ -f "$TEST_IMAGE" ]; then - ENCODE_PAGE=$(curl $CURL_OPTS -s -b "$COOKIE_JAR_USER" "$BASE_URL/encode") - CSRF_TOKEN=$(echo "$ENCODE_PAGE" | grep -oP 'name="csrf_token"[^>]*value="\K[^"]+' || echo "") + # Try encode/decode as regular user + if [ -f "$TEST_IMAGE" ]; then + ENCODE_PAGE=$(curl $CURL_OPTS -s -b "$COOKIE_JAR_USER" "$BASE_URL/encode") + CSRF_TOKEN=$(echo "$ENCODE_PAGE" | grep -oP 'name="csrf_token"[^>]*value="\K[^"]+' || echo "") - HTTP_CODE=$(curl $CURL_OPTS -s -o "$ENCODED_IMAGE_USER" -w "%{http_code}" \ - -b "$COOKIE_JAR_USER" \ - -X POST "$BASE_URL/encode" \ - -F "image=@$TEST_IMAGE" \ - -F "message=User smoke test" \ - -F "csrf_token=$CSRF_TOKEN") + HTTP_CODE=$(curl $CURL_OPTS -s -o "$ENCODED_IMAGE_USER" -w "%{http_code}" \ + -b "$COOKIE_JAR_USER" \ + -X POST "$BASE_URL/encode" \ + -F "reference_photo=@$TEST_IMAGE" \ + -F "carrier=@$TEST_IMAGE" \ + -F "message=User smoke test" \ + -F "passphrase=user test phrase" \ + -F "pin=567890" \ + -F "csrf_token=$CSRF_TOKEN") - if [ "$HTTP_CODE" = "200" ] && [ -s "$ENCODED_IMAGE_USER" ]; then - pass "Regular user encoding works" - else - fail "Regular user encoding failed" - fi - fi - else - fail "Regular user login failed" + if [ "$HTTP_CODE" = "200" ] && [ -s "$ENCODED_IMAGE_USER" ] && file "$ENCODED_IMAGE_USER" | grep -qi "image\|PNG"; then + pass "Regular user encoding works" + else + fail "Regular user encoding failed" + fi fi + else + fail "Regular user login failed" + fi else - skip "Regular user workflow (user not created)" + skip "Regular user workflow (user not created)" fi # ============================================================================= @@ -376,45 +408,45 @@ LOGIN_PAGE=$(curl $CURL_OPTS -s -c "$COOKIE_JAR" "$BASE_URL/login") CSRF_TOKEN=$(echo "$LOGIN_PAGE" | grep -oP 'name="csrf_token"[^>]*value="\K[^"]+' || echo "") curl $CURL_OPTS -s -o /dev/null \ - -b "$COOKIE_JAR" -c "$COOKIE_JAR" \ - -X POST "$BASE_URL/login" \ - -d "username=$ADMIN_USER" \ - -d "password=$ADMIN_PASS" \ - -d "csrf_token=$CSRF_TOKEN" \ - -L + -b "$COOKIE_JAR" -c "$COOKIE_JAR" \ + -X POST "$BASE_URL/login" \ + -d "username=$ADMIN_USER" \ + -d "password=$ADMIN_PASS" \ + -d "csrf_token=$CSRF_TOKEN" \ + -L # Check for recovery QR endpoint -RECOVERY_PAGE=$(curl $CURL_OPTS -s -b "$COOKIE_JAR" "$BASE_URL/recovery" 2>/dev/null || \ - curl $CURL_OPTS -s -b "$COOKIE_JAR" "$BASE_URL/settings" 2>/dev/null || \ - curl $CURL_OPTS -s -b "$COOKIE_JAR" "$BASE_URL/account" 2>/dev/null || echo "") +RECOVERY_PAGE=$(curl $CURL_OPTS -s -b "$COOKIE_JAR" "$BASE_URL/recovery" 2>/dev/null || + curl $CURL_OPTS -s -b "$COOKIE_JAR" "$BASE_URL/settings" 2>/dev/null || + curl $CURL_OPTS -s -b "$COOKIE_JAR" "$BASE_URL/account" 2>/dev/null || echo "") if echo "$RECOVERY_PAGE" | grep -qi "recovery\|qr\|backup"; then - pass "Recovery page accessible" + pass "Recovery page accessible" - # Try to get QR image - QR_URL=$(echo "$RECOVERY_PAGE" | grep -oP 'src="[^"]*qr[^"]*"' | head -1 | sed 's/src="//;s/"$//' || echo "") + # Try to get QR image + QR_URL=$(echo "$RECOVERY_PAGE" | grep -oP 'src="[^"]*qr[^"]*"' | head -1 | sed 's/src="//;s/"$//' || echo "") - if [ -n "$QR_URL" ]; then - if [[ "$QR_URL" != http* ]]; then - QR_URL="$BASE_URL$QR_URL" - fi - - HTTP_CODE=$(curl $CURL_OPTS -s -o "$QR_IMAGE" -w "%{http_code}" -b "$COOKIE_JAR" "$QR_URL") - - if [ "$HTTP_CODE" = "200" ] && [ -s "$QR_IMAGE" ]; then - if file "$QR_IMAGE" | grep -qi "image\|PNG"; then - pass "Recovery QR code generated" - else - fail "QR endpoint returned non-image" - fi - else - fail "Failed to fetch QR code" - fi - else - skip "QR code URL not found in page" + if [ -n "$QR_URL" ]; then + if [[ "$QR_URL" != http* ]]; then + QR_URL="$BASE_URL$QR_URL" fi + + HTTP_CODE=$(curl $CURL_OPTS -s -o "$QR_IMAGE" -w "%{http_code}" -b "$COOKIE_JAR" "$QR_URL") + + if [ "$HTTP_CODE" = "200" ] && [ -s "$QR_IMAGE" ]; then + if file "$QR_IMAGE" | grep -qi "image\|PNG"; then + pass "Recovery QR code generated" + else + fail "QR endpoint returned non-image" + fi + else + fail "Failed to fetch QR code" + fi + else + skip "QR code URL not found in page" + fi else - skip "Password recovery (no recovery page found)" + skip "Password recovery (no recovery page found)" fi # ============================================================================= @@ -426,30 +458,30 @@ echo -e "${BOLD}[8/9] System Health${NC}" # Check if stegasoo CLI works via SSH (optional) if command -v sshpass &>/dev/null; then - CLI_VERSION=$(sshpass -p 'stegasoo' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ - admin@$PI_IP "stegasoo --version" 2>/dev/null || echo "") + CLI_VERSION=$(sshpass -p 'stegasoo' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ + admin@$PI_IP "stegasoo --version" 2>/dev/null || echo "") - if [ -n "$CLI_VERSION" ]; then - pass "CLI accessible: $CLI_VERSION" - else - skip "CLI check (SSH failed or CLI not in PATH)" - fi + if [ -n "$CLI_VERSION" ]; then + pass "CLI accessible: $CLI_VERSION" + else + skip "CLI check (SSH failed or CLI not in PATH)" + fi else - skip "CLI check (sshpass not installed)" + skip "CLI check (sshpass not installed)" fi # Check service status via SSH if command -v sshpass &>/dev/null; then - SERVICE_STATUS=$(sshpass -p 'stegasoo' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ - admin@$PI_IP "systemctl is-active stegasoo" 2>/dev/null || echo "unknown") + SERVICE_STATUS=$(sshpass -p 'stegasoo' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ + admin@$PI_IP "systemctl is-active stegasoo" 2>/dev/null || echo "unknown") - if [ "$SERVICE_STATUS" = "active" ]; then - pass "Stegasoo service is active" - else - fail "Stegasoo service status: $SERVICE_STATUS" - fi + if [ "$SERVICE_STATUS" = "active" ]; then + pass "Stegasoo service is active" + else + fail "Stegasoo service status: $SERVICE_STATUS" + fi else - skip "Service check (sshpass not installed)" + skip "Service check (sshpass not installed)" fi # ============================================================================= @@ -461,9 +493,9 @@ echo -e "${BOLD}[9/9] Cleanup${NC}" # Just verify we can still access the site if curl $CURL_OPTS -s -o /dev/null -w "%{http_code}" "$BASE_URL" | grep -q "200\|302"; then - pass "Site still accessible after tests" + pass "Site still accessible after tests" else - fail "Site not accessible after tests" + fail "Site not accessible after tests" fi # ============================================================================= @@ -477,9 +509,9 @@ echo "" TOTAL=$((TESTS_PASSED + TESTS_FAILED)) if [ $TESTS_FAILED -eq 0 ]; then - echo -e "${GREEN}${BOLD}All tests passed!${NC} ($TESTS_PASSED/$TOTAL)" + echo -e "${GREEN}${BOLD}All tests passed!${NC} ($TESTS_PASSED/$TOTAL)" else - echo -e "${RED}${BOLD}Some tests failed${NC} ($TESTS_PASSED passed, $TESTS_FAILED failed)" + echo -e "${RED}${BOLD}Some tests failed${NC} ($TESTS_PASSED passed, $TESTS_FAILED failed)" fi echo "" diff --git a/src/stegasoo/cli.py b/src/stegasoo/cli.py index 0ac7b03..4db7b65 100644 --- a/src/stegasoo/cli.py +++ b/src/stegasoo/cli.py @@ -56,7 +56,14 @@ def cli(ctx, json_output): @cli.command() -@click.argument("image", type=click.Path(exists=True)) +@click.argument("carrier", type=click.Path(exists=True)) +@click.option( + "-r", + "--reference", + required=True, + type=click.Path(exists=True), + help="Reference photo (shared secret)", +) @click.option("-m", "--message", help="Message to encode") @click.option( "-f", @@ -86,18 +93,20 @@ def cli(ctx, json_output): @click.option("--dry-run", is_flag=True, help="Show capacity usage without encoding") @click.pass_context def encode( - ctx, image, message, file_payload, output, passphrase, pin, compress, algorithm, dry_run + ctx, carrier, reference, message, file_payload, output, passphrase, pin, compress, algorithm, dry_run ): """ Encode a message or file into an image. Examples: - stegasoo encode photo.png -m "Secret message" --passphrase --pin + stegasoo encode photo.png -r ref.jpg -m "Secret message" --passphrase --pin - stegasoo encode photo.png -f secret.pdf -o encoded.png + stegasoo encode photo.png -r ref.jpg -f secret.pdf -o encoded.png """ from PIL import Image + from .encode import encode as stegasoo_encode + from .encode import encode_file as stegasoo_encode_file if not message and not file_payload: raise click.UsageError("Either --message or --file is required") @@ -123,13 +132,14 @@ def encode( payload_type = "text" # Get image capacity - with Image.open(image) as img: + with Image.open(carrier) as img: width, height = img.size capacity_bytes = (width * height * 3 // 8) - 69 # v3.2.0: corrected overhead if dry_run: result = { - "image": image, + "carrier": carrier, + "reference": reference, "dimensions": f"{width}x{height}", "capacity_bytes": capacity_bytes, "payload_type": payload_type, @@ -142,7 +152,8 @@ def encode( if ctx.obj.get("json"): click.echo(json.dumps(result, indent=2)) else: - click.echo(f"Image: {image} ({width}x{height})") + click.echo(f"Carrier: {carrier} ({width}x{height})") + click.echo(f"Reference: {reference}") click.echo(f"Capacity: {capacity_bytes:,} bytes ({capacity_bytes//1024} KB)") click.echo(f"Payload: {payload_size:,} bytes ({payload_type})") click.echo(f"Compression: {algorithm_name(compression_algo)}") @@ -150,57 +161,159 @@ def encode( click.echo(f"Status: {'✓ Fits' if result['fits'] else '✗ Too large'}") return - # Actual encoding would happen here - # For now, show what would be done - output = output or f"{Path(image).stem}_encoded.png" + # Read input files + with open(reference, "rb") as f: + reference_data = f.read() + with open(carrier, "rb") as f: + carrier_data = f.read() - if ctx.obj.get("json"): - click.echo( - json.dumps( - { - "status": "success", - "input": image, - "output": output, - "payload_type": payload_type, - "compression": algorithm_name(compression_algo), - }, - indent=2, + # Determine output path + output = output or f"{Path(carrier).stem}_encoded.png" + + try: + if file_payload: + # Encode file + result = stegasoo_encode_file( + filepath=file_payload, + reference_photo=reference_data, + carrier_image=carrier_data, + passphrase=passphrase, + pin=pin, ) - ) - else: - click.echo(f"✓ Encoded {payload_type} to {output}") - click.echo(f" Compression: {algorithm_name(compression_algo)}") + else: + # Encode message + result = stegasoo_encode( + message=message, + reference_photo=reference_data, + carrier_image=carrier_data, + passphrase=passphrase, + pin=pin, + ) + + # Write output + with open(output, "wb") as f: + f.write(result.stego_image) + + if ctx.obj.get("json"): + click.echo( + json.dumps( + { + "status": "success", + "carrier": carrier, + "reference": reference, + "output": output, + "payload_type": payload_type, + "compression": algorithm_name(compression_algo), + }, + indent=2, + ) + ) + else: + click.echo(f"✓ Encoded {payload_type} to {output}") + click.echo(f" Reference: {reference}") + click.echo(f" Compression: {algorithm_name(compression_algo)}") + + except Exception as e: + if ctx.obj.get("json"): + click.echo(json.dumps({"status": "error", "error": str(e)}, indent=2)) + else: + click.echo(f"✗ Encoding failed: {e}", err=True) + raise SystemExit(1) @cli.command() @click.argument("image", type=click.Path(exists=True)) +@click.option( + "-r", + "--reference", + required=True, + type=click.Path(exists=True), + help="Reference photo (shared secret)", +) @click.option("--passphrase", prompt=True, hide_input=True, help="Passphrase") @click.option("--pin", prompt=True, hide_input=True, help="PIN code") @click.option("-o", "--output", type=click.Path(), help="Output path for file payloads") @click.pass_context -def decode(ctx, image, passphrase, pin, output): +def decode(ctx, image, reference, passphrase, pin, output): """ Decode a message or file from an image. Examples: - stegasoo decode encoded.png --passphrase --pin + stegasoo decode encoded.png -r ref.jpg --passphrase --pin - stegasoo decode encoded.png -o ./extracted/ + stegasoo decode encoded.png -r ref.jpg -o ./extracted/ """ - # Actual decoding would happen here - result = { - "status": "success", - "image": image, - "payload_type": "text", - "message": "[Decoded message would appear here]", - } + from .decode import decode as stegasoo_decode - if ctx.obj.get("json"): - click.echo(json.dumps(result, indent=2)) - else: - click.echo(f"Decoded from {image}:") - click.echo(result["message"]) + # Read input files + with open(image, "rb") as f: + stego_data = f.read() + with open(reference, "rb") as f: + reference_data = f.read() + + try: + result = stegasoo_decode( + stego_image=stego_data, + reference_photo=reference_data, + passphrase=passphrase, + pin=pin, + ) + + if result.is_file: + # File payload + filename = result.filename or "decoded_file" + output_path = Path(output) / filename if output else Path(filename) + + # Ensure output directory exists + output_path.parent.mkdir(parents=True, exist_ok=True) + + with open(output_path, "wb") as f: + f.write(result.file_data) + + if ctx.obj.get("json"): + click.echo( + json.dumps( + { + "status": "success", + "image": image, + "reference": reference, + "payload_type": "file", + "filename": filename, + "output": str(output_path), + "size": len(result.file_data), + }, + indent=2, + ) + ) + else: + click.echo(f"✓ Extracted file: {output_path}") + click.echo(f" Size: {len(result.file_data):,} bytes") + else: + # Text message + if ctx.obj.get("json"): + click.echo( + json.dumps( + { + "status": "success", + "image": image, + "reference": reference, + "payload_type": "text", + "message": result.message, + }, + indent=2, + ) + ) + else: + click.echo(f"Decoded from {image}:") + click.echo(result.message) + + except Exception as e: + if ctx.obj.get("json"): + click.echo(json.dumps({"status": "error", "error": str(e)}, indent=2)) + else: + click.echo(f"✗ Decoding failed: {e}", err=True) + raise SystemExit(1) # =============================================================================