Implement CLI encode/decode with reference photo support
- Add required -r/--reference option to encode command - Add required -r/--reference option to decode command - Replace stub implementations with actual library calls - CLI now properly encodes and decodes messages/files - Fix smoke test form field names and add proper redirect handling The CLI encode/decode were stubs that didn't actually work. Now they properly use the stegasoo library functions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,23 +20,23 @@ NC='\033[0m'
|
|||||||
PI_IP="${1:-192.168.0.4}"
|
PI_IP="${1:-192.168.0.4}"
|
||||||
HTTPS=false
|
HTTPS=false
|
||||||
if [[ "$2" == "--https" ]] || [[ "$1" == "--https" ]]; then
|
if [[ "$2" == "--https" ]] || [[ "$1" == "--https" ]]; then
|
||||||
HTTPS=true
|
HTTPS=true
|
||||||
if [[ "$1" == "--https" ]]; then
|
if [[ "$1" == "--https" ]]; then
|
||||||
PI_IP="192.168.0.4"
|
PI_IP="192.168.0.4"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$HTTPS" = true ]; then
|
if [ "$HTTPS" = true ]; then
|
||||||
BASE_URL="https://$PI_IP:5000"
|
BASE_URL="https://$PI_IP:5000"
|
||||||
CURL_OPTS="-k" # Allow self-signed certs
|
CURL_OPTS="-k" # Allow self-signed certs
|
||||||
else
|
else
|
||||||
BASE_URL="http://$PI_IP:5000"
|
BASE_URL="http://$PI_IP:5000"
|
||||||
CURL_OPTS=""
|
CURL_OPTS=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Test credentials
|
# Test credentials
|
||||||
ADMIN_USER="smokeadmin"
|
ADMIN_USER="admin"
|
||||||
ADMIN_PASS="SmokeAdmin123!"
|
ADMIN_PASS="stegasoo"
|
||||||
REGULAR_USER="smokeuser"
|
REGULAR_USER="smokeuser"
|
||||||
REGULAR_PASS="SmokeUser123!"
|
REGULAR_PASS="SmokeUser123!"
|
||||||
|
|
||||||
@@ -51,24 +51,24 @@ ENCODED_IMAGE_USER=$(mktemp --suffix=.png)
|
|||||||
QR_IMAGE=$(mktemp --suffix=.png)
|
QR_IMAGE=$(mktemp --suffix=.png)
|
||||||
|
|
||||||
cleanup() {
|
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
|
trap cleanup EXIT
|
||||||
|
|
||||||
# Create a simple test image (red square)
|
# Create a simple test image (red square)
|
||||||
create_test_image() {
|
create_test_image() {
|
||||||
if command -v convert &>/dev/null; then
|
if command -v convert &>/dev/null; then
|
||||||
convert -size 100x100 xc:red "$TEST_IMAGE"
|
convert -size 100x100 xc:red "$TEST_IMAGE"
|
||||||
elif command -v python3 &>/dev/null; then
|
elif command -v python3 &>/dev/null; then
|
||||||
python3 -c "
|
python3 -c "
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
img = Image.new('RGB', (100, 100), color='red')
|
img = Image.new('RGB', (100, 100), color='red')
|
||||||
img.save('$TEST_IMAGE')
|
img.save('$TEST_IMAGE')
|
||||||
"
|
"
|
||||||
else
|
else
|
||||||
echo -e "${YELLOW}Warning: No image tool available, skipping encode/decode tests${NC}"
|
echo -e "${YELLOW}Warning: No image tool available, skipping encode/decode tests${NC}"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Results tracking
|
# Results tracking
|
||||||
@@ -76,17 +76,17 @@ TESTS_PASSED=0
|
|||||||
TESTS_FAILED=0
|
TESTS_FAILED=0
|
||||||
|
|
||||||
pass() {
|
pass() {
|
||||||
echo -e " ${GREEN}[PASS]${NC} $1"
|
echo -e " ${GREEN}[PASS]${NC} $1"
|
||||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
fail() {
|
fail() {
|
||||||
echo -e " ${RED}[FAIL]${NC} $1"
|
echo -e " ${RED}[FAIL]${NC} $1"
|
||||||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
skip() {
|
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}"
|
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
|
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
|
else
|
||||||
fail "Web UI not reachable at $BASE_URL"
|
fail "Web UI not reachable at $BASE_URL"
|
||||||
echo -e "${RED}Cannot continue without web access. Is the Pi running?${NC}"
|
echo -e "${RED}Cannot continue without web access. Is the Pi running?${NC}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if redirected to setup (first run) or login
|
# Check if redirected to setup (first run) or login
|
||||||
REDIRECT=$(curl $CURL_OPTS -s -o /dev/null -w "%{redirect_url}" "$BASE_URL")
|
REDIRECT=$(curl $CURL_OPTS -s -o /dev/null -w "%{redirect_url}" "$BASE_URL")
|
||||||
if echo "$REDIRECT" | grep -q "setup"; then
|
if echo "$REDIRECT" | grep -q "setup"; then
|
||||||
pass "Redirected to setup (fresh install)"
|
pass "Redirected to setup (fresh install)"
|
||||||
NEEDS_SETUP=true
|
NEEDS_SETUP=true
|
||||||
elif echo "$REDIRECT" | grep -q "login"; then
|
elif echo "$REDIRECT" | grep -q "login"; then
|
||||||
pass "Redirected to login (already configured)"
|
pass "Redirected to login (already configured)"
|
||||||
NEEDS_SETUP=false
|
NEEDS_SETUP=false
|
||||||
else
|
else
|
||||||
# Check page content
|
# Check page content
|
||||||
if curl $CURL_OPTS -s "$BASE_URL" | grep -q "setup\|Setup\|Create.*Admin"; then
|
if curl $CURL_OPTS -s "$BASE_URL" | grep -q "setup\|Setup\|Create.*Admin"; then
|
||||||
pass "Setup page detected"
|
pass "Setup page detected"
|
||||||
NEEDS_SETUP=true
|
NEEDS_SETUP=true
|
||||||
else
|
else
|
||||||
pass "Login page detected"
|
pass "Login page detected"
|
||||||
NEEDS_SETUP=false
|
NEEDS_SETUP=false
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
@@ -142,35 +142,35 @@ echo ""
|
|||||||
echo -e "${BOLD}[2/9] Admin Setup${NC}"
|
echo -e "${BOLD}[2/9] Admin Setup${NC}"
|
||||||
|
|
||||||
if [ "$NEEDS_SETUP" = true ]; then
|
if [ "$NEEDS_SETUP" = true ]; then
|
||||||
# Get CSRF token from setup page
|
# Get CSRF token from setup page
|
||||||
SETUP_PAGE=$(curl $CURL_OPTS -s -c "$COOKIE_JAR" "$BASE_URL/setup")
|
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 "")
|
CSRF_TOKEN=$(echo "$SETUP_PAGE" | grep -oP 'name="csrf_token"[^>]*value="\K[^"]+' || echo "")
|
||||||
|
|
||||||
if [ -z "$CSRF_TOKEN" ]; then
|
if [ -z "$CSRF_TOKEN" ]; then
|
||||||
# Try alternate pattern
|
# Try alternate pattern
|
||||||
CSRF_TOKEN=$(echo "$SETUP_PAGE" | grep -oP 'csrf_token.*?value="\K[^"]+' || echo "")
|
CSRF_TOKEN=$(echo "$SETUP_PAGE" | grep -oP 'csrf_token.*?value="\K[^"]+' || echo "")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create admin user
|
# Create admin user
|
||||||
HTTP_CODE=$(curl $CURL_OPTS -s -o "$RESPONSE" -w "%{http_code}" \
|
HTTP_CODE=$(curl $CURL_OPTS -s -o "$RESPONSE" -w "%{http_code}" \
|
||||||
-b "$COOKIE_JAR" -c "$COOKIE_JAR" \
|
-b "$COOKIE_JAR" -c "$COOKIE_JAR" \
|
||||||
-X POST "$BASE_URL/setup" \
|
-X POST "$BASE_URL/setup" \
|
||||||
-d "username=$ADMIN_USER" \
|
-d "username=$ADMIN_USER" \
|
||||||
-d "password=$ADMIN_PASS" \
|
-d "password=$ADMIN_PASS" \
|
||||||
-d "password_confirm=$ADMIN_PASS" \
|
-d "password_confirm=$ADMIN_PASS" \
|
||||||
-d "csrf_token=$CSRF_TOKEN")
|
-d "csrf_token=$CSRF_TOKEN")
|
||||||
|
|
||||||
if [ "$HTTP_CODE" = "302" ] || [ "$HTTP_CODE" = "200" ]; then
|
if [ "$HTTP_CODE" = "302" ] || [ "$HTTP_CODE" = "200" ]; then
|
||||||
if curl $CURL_OPTS -s "$BASE_URL" | grep -q "login\|Login"; then
|
if curl $CURL_OPTS -s "$BASE_URL" | grep -q "login\|Login"; then
|
||||||
pass "Admin user created successfully"
|
pass "Admin user created successfully"
|
||||||
else
|
|
||||||
pass "Setup completed (assuming success)"
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
fail "Failed to create admin user (HTTP $HTTP_CODE)"
|
pass "Setup completed (assuming success)"
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
|
fail "Failed to create admin user (HTTP $HTTP_CODE)"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
skip "Setup already complete"
|
skip "Setup already complete"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
@@ -186,20 +186,20 @@ CSRF_TOKEN=$(echo "$LOGIN_PAGE" | grep -oP 'name="csrf_token"[^>]*value="\K[^"]+
|
|||||||
|
|
||||||
# Try login as admin
|
# Try login as admin
|
||||||
HTTP_CODE=$(curl $CURL_OPTS -s -o "$RESPONSE" -w "%{http_code}" \
|
HTTP_CODE=$(curl $CURL_OPTS -s -o "$RESPONSE" -w "%{http_code}" \
|
||||||
-b "$COOKIE_JAR" -c "$COOKIE_JAR" \
|
-b "$COOKIE_JAR" -c "$COOKIE_JAR" \
|
||||||
-X POST "$BASE_URL/login" \
|
-X POST "$BASE_URL/login" \
|
||||||
-d "username=$ADMIN_USER" \
|
-d "username=$ADMIN_USER" \
|
||||||
-d "password=$ADMIN_PASS" \
|
-d "password=$ADMIN_PASS" \
|
||||||
-d "csrf_token=$CSRF_TOKEN" \
|
-d "csrf_token=$CSRF_TOKEN" \
|
||||||
-L)
|
-L)
|
||||||
|
|
||||||
# Check if we're logged in by accessing a protected page
|
# 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
|
if curl $CURL_OPTS -s -b "$COOKIE_JAR" "$BASE_URL/" | grep -qi "encode\|decode\|logout"; then
|
||||||
pass "Admin login successful"
|
pass "Admin login successful"
|
||||||
ADMIN_LOGGED_IN=true
|
ADMIN_LOGGED_IN=true
|
||||||
else
|
else
|
||||||
fail "Admin login failed"
|
fail "Admin login failed"
|
||||||
ADMIN_LOGGED_IN=false
|
ADMIN_LOGGED_IN=false
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
@@ -210,55 +210,84 @@ echo ""
|
|||||||
echo -e "${BOLD}[4/9] Admin Encode/Decode${NC}"
|
echo -e "${BOLD}[4/9] Admin Encode/Decode${NC}"
|
||||||
|
|
||||||
if [ "$ADMIN_LOGGED_IN" = true ]; then
|
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
|
if echo "$ENCODE_PAGE" | grep -qi "encode\|message\|image\|upload"; then
|
||||||
pass "Encode page loads"
|
pass "Encode page loads"
|
||||||
else
|
else
|
||||||
fail "Encode page not accessible"
|
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
|
fi
|
||||||
|
|
||||||
# Try actual encoding if we have image tools
|
if [ -n "$DOWNLOAD_URL" ]; then
|
||||||
if create_test_image 2>/dev/null; then
|
# Download the encoded image
|
||||||
CSRF_TOKEN=$(echo "$ENCODE_PAGE" | grep -oP 'name="csrf_token"[^>]*value="\K[^"]+' || echo "")
|
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}" \
|
if [ "$HTTP_CODE" = "200" ] && file "$ENCODED_IMAGE" | grep -qi "image\|PNG\|JPEG"; then
|
||||||
-b "$COOKIE_JAR" \
|
pass "Admin encoding works"
|
||||||
-X POST "$BASE_URL/encode" \
|
|
||||||
-F "image=@$TEST_IMAGE" \
|
|
||||||
-F "message=Admin smoke test" \
|
|
||||||
-F "csrf_token=$CSRF_TOKEN")
|
|
||||||
|
|
||||||
if [ "$HTTP_CODE" = "200" ] && [ -s "$ENCODED_IMAGE" ]; then
|
# Now decode it
|
||||||
if file "$ENCODED_IMAGE" | grep -qi "image\|PNG\|JPEG"; then
|
DECODE_PAGE=$(curl $CURL_OPTS -s -b "$COOKIE_JAR" "$BASE_URL/decode")
|
||||||
pass "Admin encoding works"
|
CSRF_TOKEN=$(echo "$DECODE_PAGE" | grep -oP 'name="csrf_token"[^>]*value="\K[^"]+' || echo "")
|
||||||
|
|
||||||
# Now decode it
|
DECODED=$(curl $CURL_OPTS -s \
|
||||||
DECODE_PAGE=$(curl $CURL_OPTS -s -b "$COOKIE_JAR" "$BASE_URL/decode")
|
-b "$COOKIE_JAR" \
|
||||||
CSRF_TOKEN=$(echo "$DECODE_PAGE" | grep -oP 'name="csrf_token"[^>]*value="\K[^"]+' || echo "")
|
-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 \
|
if echo "$DECODED" | grep -q "Admin smoke test"; then
|
||||||
-b "$COOKIE_JAR" \
|
pass "Admin decoding works"
|
||||||
-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
|
|
||||||
else
|
else
|
||||||
fail "Encoding request failed (HTTP $HTTP_CODE)"
|
fail "Admin decode failed"
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
|
fail "Failed to download encoded image (HTTP $HTTP_CODE)"
|
||||||
|
fi
|
||||||
else
|
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
|
fi
|
||||||
|
else
|
||||||
|
skip "Encode/Decode (no image tools)"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
skip "Admin encode/decode (not logged in)"
|
skip "Admin encode/decode (not logged in)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
@@ -269,48 +298,48 @@ echo ""
|
|||||||
echo -e "${BOLD}[5/9] Create Regular User${NC}"
|
echo -e "${BOLD}[5/9] Create Regular User${NC}"
|
||||||
|
|
||||||
if [ "$ADMIN_LOGGED_IN" = true ]; then
|
if [ "$ADMIN_LOGGED_IN" = true ]; then
|
||||||
# Check if there's a user management page
|
# Check if there's a user management page
|
||||||
USERS_PAGE=$(curl $CURL_OPTS -s -b "$COOKIE_JAR" "$BASE_URL/users" 2>/dev/null || echo "")
|
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
|
if echo "$USERS_PAGE" | grep -qi "user\|create\|add"; then
|
||||||
CSRF_TOKEN=$(echo "$USERS_PAGE" | grep -oP 'name="csrf_token"[^>]*value="\K[^"]+' || echo "")
|
CSRF_TOKEN=$(echo "$USERS_PAGE" | grep -oP 'name="csrf_token"[^>]*value="\K[^"]+' || echo "")
|
||||||
|
|
||||||
HTTP_CODE=$(curl $CURL_OPTS -s -o "$RESPONSE" -w "%{http_code}" \
|
HTTP_CODE=$(curl $CURL_OPTS -s -o "$RESPONSE" -w "%{http_code}" \
|
||||||
-b "$COOKIE_JAR" \
|
-b "$COOKIE_JAR" \
|
||||||
-X POST "$BASE_URL/users/create" \
|
-X POST "$BASE_URL/users/create" \
|
||||||
-d "username=$REGULAR_USER" \
|
-d "username=$REGULAR_USER" \
|
||||||
-d "password=$REGULAR_PASS" \
|
-d "password=$REGULAR_PASS" \
|
||||||
-d "password_confirm=$REGULAR_PASS" \
|
-d "password_confirm=$REGULAR_PASS" \
|
||||||
-d "csrf_token=$CSRF_TOKEN")
|
-d "csrf_token=$CSRF_TOKEN")
|
||||||
|
|
||||||
if [ "$HTTP_CODE" = "302" ] || [ "$HTTP_CODE" = "200" ]; then
|
if [ "$HTTP_CODE" = "302" ] || [ "$HTTP_CODE" = "200" ]; then
|
||||||
pass "Regular user created"
|
pass "Regular user created"
|
||||||
USER_CREATED=true
|
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
|
|
||||||
else
|
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
|
USER_CREATED=false
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
skip "User creation (admin not logged in)"
|
skip "User creation (no user management page)"
|
||||||
USER_CREATED=false
|
USER_CREATED=false
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
skip "User creation (admin not logged in)"
|
||||||
|
USER_CREATED=false
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
@@ -321,47 +350,50 @@ echo ""
|
|||||||
echo -e "${BOLD}[6/9] Regular User Workflow${NC}"
|
echo -e "${BOLD}[6/9] Regular User Workflow${NC}"
|
||||||
|
|
||||||
if [ "$USER_CREATED" = true ]; then
|
if [ "$USER_CREATED" = true ]; then
|
||||||
# Logout admin first (get fresh session)
|
# Logout admin first (get fresh session)
|
||||||
curl $CURL_OPTS -s -b "$COOKIE_JAR" "$BASE_URL/logout" > /dev/null
|
curl $CURL_OPTS -s -b "$COOKIE_JAR" "$BASE_URL/logout" >/dev/null
|
||||||
|
|
||||||
# Login as regular user
|
# Login as regular user
|
||||||
LOGIN_PAGE=$(curl $CURL_OPTS -s -c "$COOKIE_JAR_USER" "$BASE_URL/login")
|
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 "")
|
CSRF_TOKEN=$(echo "$LOGIN_PAGE" | grep -oP 'name="csrf_token"[^>]*value="\K[^"]+' || echo "")
|
||||||
|
|
||||||
HTTP_CODE=$(curl $CURL_OPTS -s -o "$RESPONSE" -w "%{http_code}" \
|
HTTP_CODE=$(curl $CURL_OPTS -s -o "$RESPONSE" -w "%{http_code}" \
|
||||||
-b "$COOKIE_JAR_USER" -c "$COOKIE_JAR_USER" \
|
-b "$COOKIE_JAR_USER" -c "$COOKIE_JAR_USER" \
|
||||||
-X POST "$BASE_URL/login" \
|
-X POST "$BASE_URL/login" \
|
||||||
-d "username=$REGULAR_USER" \
|
-d "username=$REGULAR_USER" \
|
||||||
-d "password=$REGULAR_PASS" \
|
-d "password=$REGULAR_PASS" \
|
||||||
-d "csrf_token=$CSRF_TOKEN" \
|
-d "csrf_token=$CSRF_TOKEN" \
|
||||||
-L)
|
-L)
|
||||||
|
|
||||||
if curl $CURL_OPTS -s -b "$COOKIE_JAR_USER" "$BASE_URL/" | grep -qi "encode\|decode\|logout"; then
|
if curl $CURL_OPTS -s -b "$COOKIE_JAR_USER" "$BASE_URL/" | grep -qi "encode\|decode\|logout"; then
|
||||||
pass "Regular user login successful"
|
pass "Regular user login successful"
|
||||||
|
|
||||||
# Try encode/decode as regular user
|
# Try encode/decode as regular user
|
||||||
if [ -f "$TEST_IMAGE" ]; then
|
if [ -f "$TEST_IMAGE" ]; then
|
||||||
ENCODE_PAGE=$(curl $CURL_OPTS -s -b "$COOKIE_JAR_USER" "$BASE_URL/encode")
|
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 "")
|
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}" \
|
HTTP_CODE=$(curl $CURL_OPTS -s -o "$ENCODED_IMAGE_USER" -w "%{http_code}" \
|
||||||
-b "$COOKIE_JAR_USER" \
|
-b "$COOKIE_JAR_USER" \
|
||||||
-X POST "$BASE_URL/encode" \
|
-X POST "$BASE_URL/encode" \
|
||||||
-F "image=@$TEST_IMAGE" \
|
-F "reference_photo=@$TEST_IMAGE" \
|
||||||
-F "message=User smoke test" \
|
-F "carrier=@$TEST_IMAGE" \
|
||||||
-F "csrf_token=$CSRF_TOKEN")
|
-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
|
if [ "$HTTP_CODE" = "200" ] && [ -s "$ENCODED_IMAGE_USER" ] && file "$ENCODED_IMAGE_USER" | grep -qi "image\|PNG"; then
|
||||||
pass "Regular user encoding works"
|
pass "Regular user encoding works"
|
||||||
else
|
else
|
||||||
fail "Regular user encoding failed"
|
fail "Regular user encoding failed"
|
||||||
fi
|
fi
|
||||||
fi
|
|
||||||
else
|
|
||||||
fail "Regular user login failed"
|
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
|
fail "Regular user login failed"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
skip "Regular user workflow (user not created)"
|
skip "Regular user workflow (user not created)"
|
||||||
fi
|
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 "")
|
CSRF_TOKEN=$(echo "$LOGIN_PAGE" | grep -oP 'name="csrf_token"[^>]*value="\K[^"]+' || echo "")
|
||||||
|
|
||||||
curl $CURL_OPTS -s -o /dev/null \
|
curl $CURL_OPTS -s -o /dev/null \
|
||||||
-b "$COOKIE_JAR" -c "$COOKIE_JAR" \
|
-b "$COOKIE_JAR" -c "$COOKIE_JAR" \
|
||||||
-X POST "$BASE_URL/login" \
|
-X POST "$BASE_URL/login" \
|
||||||
-d "username=$ADMIN_USER" \
|
-d "username=$ADMIN_USER" \
|
||||||
-d "password=$ADMIN_PASS" \
|
-d "password=$ADMIN_PASS" \
|
||||||
-d "csrf_token=$CSRF_TOKEN" \
|
-d "csrf_token=$CSRF_TOKEN" \
|
||||||
-L
|
-L
|
||||||
|
|
||||||
# Check for recovery QR endpoint
|
# Check for recovery QR endpoint
|
||||||
RECOVERY_PAGE=$(curl $CURL_OPTS -s -b "$COOKIE_JAR" "$BASE_URL/recovery" 2>/dev/null || \
|
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/settings" 2>/dev/null ||
|
||||||
curl $CURL_OPTS -s -b "$COOKIE_JAR" "$BASE_URL/account" 2>/dev/null || echo "")
|
curl $CURL_OPTS -s -b "$COOKIE_JAR" "$BASE_URL/account" 2>/dev/null || echo "")
|
||||||
|
|
||||||
if echo "$RECOVERY_PAGE" | grep -qi "recovery\|qr\|backup"; then
|
if echo "$RECOVERY_PAGE" | grep -qi "recovery\|qr\|backup"; then
|
||||||
pass "Recovery page accessible"
|
pass "Recovery page accessible"
|
||||||
|
|
||||||
# Try to get QR image
|
# Try to get QR image
|
||||||
QR_URL=$(echo "$RECOVERY_PAGE" | grep -oP 'src="[^"]*qr[^"]*"' | head -1 | sed 's/src="//;s/"$//' || echo "")
|
QR_URL=$(echo "$RECOVERY_PAGE" | grep -oP 'src="[^"]*qr[^"]*"' | head -1 | sed 's/src="//;s/"$//' || echo "")
|
||||||
|
|
||||||
if [ -n "$QR_URL" ]; then
|
if [ -n "$QR_URL" ]; then
|
||||||
if [[ "$QR_URL" != http* ]]; then
|
if [[ "$QR_URL" != http* ]]; then
|
||||||
QR_URL="$BASE_URL$QR_URL"
|
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
|
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
|
else
|
||||||
skip "Password recovery (no recovery page found)"
|
skip "Password recovery (no recovery page found)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
@@ -426,30 +458,30 @@ echo -e "${BOLD}[8/9] System Health${NC}"
|
|||||||
|
|
||||||
# Check if stegasoo CLI works via SSH (optional)
|
# Check if stegasoo CLI works via SSH (optional)
|
||||||
if command -v sshpass &>/dev/null; then
|
if command -v sshpass &>/dev/null; then
|
||||||
CLI_VERSION=$(sshpass -p 'stegasoo' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
|
CLI_VERSION=$(sshpass -p 'stegasoo' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
|
||||||
admin@$PI_IP "stegasoo --version" 2>/dev/null || echo "")
|
admin@$PI_IP "stegasoo --version" 2>/dev/null || echo "")
|
||||||
|
|
||||||
if [ -n "$CLI_VERSION" ]; then
|
if [ -n "$CLI_VERSION" ]; then
|
||||||
pass "CLI accessible: $CLI_VERSION"
|
pass "CLI accessible: $CLI_VERSION"
|
||||||
else
|
else
|
||||||
skip "CLI check (SSH failed or CLI not in PATH)"
|
skip "CLI check (SSH failed or CLI not in PATH)"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
skip "CLI check (sshpass not installed)"
|
skip "CLI check (sshpass not installed)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check service status via SSH
|
# Check service status via SSH
|
||||||
if command -v sshpass &>/dev/null; then
|
if command -v sshpass &>/dev/null; then
|
||||||
SERVICE_STATUS=$(sshpass -p 'stegasoo' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
|
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")
|
admin@$PI_IP "systemctl is-active stegasoo" 2>/dev/null || echo "unknown")
|
||||||
|
|
||||||
if [ "$SERVICE_STATUS" = "active" ]; then
|
if [ "$SERVICE_STATUS" = "active" ]; then
|
||||||
pass "Stegasoo service is active"
|
pass "Stegasoo service is active"
|
||||||
else
|
else
|
||||||
fail "Stegasoo service status: $SERVICE_STATUS"
|
fail "Stegasoo service status: $SERVICE_STATUS"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
skip "Service check (sshpass not installed)"
|
skip "Service check (sshpass not installed)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
@@ -461,9 +493,9 @@ echo -e "${BOLD}[9/9] Cleanup${NC}"
|
|||||||
|
|
||||||
# Just verify we can still access the site
|
# 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
|
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
|
else
|
||||||
fail "Site not accessible after tests"
|
fail "Site not accessible after tests"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
@@ -477,9 +509,9 @@ echo ""
|
|||||||
TOTAL=$((TESTS_PASSED + TESTS_FAILED))
|
TOTAL=$((TESTS_PASSED + TESTS_FAILED))
|
||||||
|
|
||||||
if [ $TESTS_FAILED -eq 0 ]; then
|
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
|
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
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@@ -56,7 +56,14 @@ def cli(ctx, json_output):
|
|||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@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("-m", "--message", help="Message to encode")
|
||||||
@click.option(
|
@click.option(
|
||||||
"-f",
|
"-f",
|
||||||
@@ -86,18 +93,20 @@ def cli(ctx, json_output):
|
|||||||
@click.option("--dry-run", is_flag=True, help="Show capacity usage without encoding")
|
@click.option("--dry-run", is_flag=True, help="Show capacity usage without encoding")
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def encode(
|
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.
|
Encode a message or file into an image.
|
||||||
|
|
||||||
Examples:
|
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 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:
|
if not message and not file_payload:
|
||||||
raise click.UsageError("Either --message or --file is required")
|
raise click.UsageError("Either --message or --file is required")
|
||||||
@@ -123,13 +132,14 @@ def encode(
|
|||||||
payload_type = "text"
|
payload_type = "text"
|
||||||
|
|
||||||
# Get image capacity
|
# Get image capacity
|
||||||
with Image.open(image) as img:
|
with Image.open(carrier) as img:
|
||||||
width, height = img.size
|
width, height = img.size
|
||||||
capacity_bytes = (width * height * 3 // 8) - 69 # v3.2.0: corrected overhead
|
capacity_bytes = (width * height * 3 // 8) - 69 # v3.2.0: corrected overhead
|
||||||
|
|
||||||
if dry_run:
|
if dry_run:
|
||||||
result = {
|
result = {
|
||||||
"image": image,
|
"carrier": carrier,
|
||||||
|
"reference": reference,
|
||||||
"dimensions": f"{width}x{height}",
|
"dimensions": f"{width}x{height}",
|
||||||
"capacity_bytes": capacity_bytes,
|
"capacity_bytes": capacity_bytes,
|
||||||
"payload_type": payload_type,
|
"payload_type": payload_type,
|
||||||
@@ -142,7 +152,8 @@ def encode(
|
|||||||
if ctx.obj.get("json"):
|
if ctx.obj.get("json"):
|
||||||
click.echo(json.dumps(result, indent=2))
|
click.echo(json.dumps(result, indent=2))
|
||||||
else:
|
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"Capacity: {capacity_bytes:,} bytes ({capacity_bytes//1024} KB)")
|
||||||
click.echo(f"Payload: {payload_size:,} bytes ({payload_type})")
|
click.echo(f"Payload: {payload_size:,} bytes ({payload_type})")
|
||||||
click.echo(f"Compression: {algorithm_name(compression_algo)}")
|
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'}")
|
click.echo(f"Status: {'✓ Fits' if result['fits'] else '✗ Too large'}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Actual encoding would happen here
|
# Read input files
|
||||||
# For now, show what would be done
|
with open(reference, "rb") as f:
|
||||||
output = output or f"{Path(image).stem}_encoded.png"
|
reference_data = f.read()
|
||||||
|
with open(carrier, "rb") as f:
|
||||||
|
carrier_data = f.read()
|
||||||
|
|
||||||
if ctx.obj.get("json"):
|
# Determine output path
|
||||||
click.echo(
|
output = output or f"{Path(carrier).stem}_encoded.png"
|
||||||
json.dumps(
|
|
||||||
{
|
try:
|
||||||
"status": "success",
|
if file_payload:
|
||||||
"input": image,
|
# Encode file
|
||||||
"output": output,
|
result = stegasoo_encode_file(
|
||||||
"payload_type": payload_type,
|
filepath=file_payload,
|
||||||
"compression": algorithm_name(compression_algo),
|
reference_photo=reference_data,
|
||||||
},
|
carrier_image=carrier_data,
|
||||||
indent=2,
|
passphrase=passphrase,
|
||||||
|
pin=pin,
|
||||||
)
|
)
|
||||||
)
|
else:
|
||||||
else:
|
# Encode message
|
||||||
click.echo(f"✓ Encoded {payload_type} to {output}")
|
result = stegasoo_encode(
|
||||||
click.echo(f" Compression: {algorithm_name(compression_algo)}")
|
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()
|
@cli.command()
|
||||||
@click.argument("image", type=click.Path(exists=True))
|
@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("--passphrase", prompt=True, hide_input=True, help="Passphrase")
|
||||||
@click.option("--pin", prompt=True, hide_input=True, help="PIN code")
|
@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.option("-o", "--output", type=click.Path(), help="Output path for file payloads")
|
||||||
@click.pass_context
|
@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.
|
Decode a message or file from an image.
|
||||||
|
|
||||||
Examples:
|
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
|
from .decode import decode as stegasoo_decode
|
||||||
result = {
|
|
||||||
"status": "success",
|
|
||||||
"image": image,
|
|
||||||
"payload_type": "text",
|
|
||||||
"message": "[Decoded message would appear here]",
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.obj.get("json"):
|
# Read input files
|
||||||
click.echo(json.dumps(result, indent=2))
|
with open(image, "rb") as f:
|
||||||
else:
|
stego_data = f.read()
|
||||||
click.echo(f"Decoded from {image}:")
|
with open(reference, "rb") as f:
|
||||||
click.echo(result["message"])
|
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)
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|||||||
Reference in New Issue
Block a user